周赛Round30
D.小红整数操作
首先一个很显然的思路是算出满足大于等于l的最小的x需要乘几,再算出满足小于等于r的最大的y需要乘几,然后这两个数之差就能得到有几组可行的x,y。开始就是这样写的,但是挂了 因为问题是这个思路默认了x,y开始都小于l,r,但这是不一定的。在赛场上意识到这个问题后我一度想分类讨论x,y和l,r的大小关系,但情况太多,显然也是不可行的。 其实仔细看一下,我们除了乘a之外,还可以除a,那我们完全可以先把x,y除到最小,再开始乘,就可以按上面的思路来了,而除到最小需要除gcd(x,y)。
void solve(void){
int x,y,l,r,low,up;
cin>>x>>y>>l>>r;
if(x>y)swap(x,y);
int d=__gcd(x,y);
x/=d;
y/=d;
int mn=l/x;
if(l%x)mn++;
int mx=r/y;
if(mx>=mn)cout<<mx-mn+1;
else cout<<0;
}
E.小红树上染色
树形dp,没什么好说的,和 没有上司的舞会 很像,不能有相邻的白色节点,那么如果当前根节点为白色,所有儿子都必须为红,如果根节点为红,儿子可以为白也可以为红。 但是得到了一个惨痛的教训:%的优先级比*/低,但是比+-高,所以a*b%mod是可以的,但是a+b%mod是不行的,必须写成(a+b)%mod
__int128 cnt[N][2];
void dfs(int u,int f){
cnt[u][0]=1;
cnt[u][1]=1;
for(int v:a[u]){
if(v==f)continue;
dfs(v,u);
cnt[u][0]=cnt[u][0]*cnt[v][1]%mod;
cnt[u][1]=cnt[u][1]*(cnt[v][1]+cnt[v][0])%mod;
}
// cout<<u<<' '<<cnt[u][0]<<' '<<cnt[u][1]<<'\n';
}
void solve(void){
int n;
cin>>n;
rep(i,1,n-1) {
int u,v;
cin>>u>>v;
a[u].push_back(v);
a[v].push_back(u);
}
dfs(1,0);
write((cnt[1][0]+cnt[1][1])%mod);
}
F.小红又又又战小紫
概率dp,需要注意的关键是这是从末态往初态推的,并且每一次转移轮数都要加一。具体来说,dp(i,j)表示小红有i张一攻卡,小紫有j张一攻卡时,剩余轮数的期望,此时如果小红抽到一攻卡,小紫抽到二攻卡,小紫的卡会干掉小红的卡,会转移到dp(i-1,j),如果小紫抽到一攻,小红抽到二攻,会转移到dp(i,j-1),如果抽到卡攻击相等,还会转移到dp(i,j),因此有
dp(i,j)=(1-p2-p3)*dp(i,j)+p2*dp(i-1,j)+p3*dp(i,j-1)+1
整理得
dp(i,j)=(p2*dp(i-1,j)+p3*dp(i,j-1)+1)/(p2+p3)
具体实现时还需要注意,每进行一步操作,都要取模,并且对于除法需要使用乘法逆元。
cin>>n>>m;
int cnt[2][3]={0};
int dp[55][55]={0};
for(int i=1;i<=n;i++){
int t;
cin>>t;
cnt[0][t]++;
}
for(int i=1;i<=m;i++){
int t;
cin>>t;
cnt[1][t]++;
}
for(int i=0;i<=n;i++)
for(int j=0;j<=m;j++){
int p2=i*inv(i+cnt[0][2])%mod*(cnt[1][2]*inv(j+cnt[1][2])%mod)%mod;
int p3=cnt[0][2]*inv(i+cnt[0][2])%mod*(j*inv(j+cnt[1][2])%mod)%mod;
dp[i][j]=(p2*dp[i-1][j]%mod+p3*dp[i][j-1]%mod+1)%mod*inv(p2+p3)%mod;
}
cout<<dp[cnt[0][1]][cnt[1][1]];