分配颜色
题目描述
今天小A带同学们玩游戏,需要分为两组。每名同学为红队或蓝队。
为了方便分配颜色,小A让同学们站成n排m列。初始时,所有人都为红队成员。
小A可以进行p次操作1和q次操作2,操作解释如下:
- 操作1:把某一行的同学进行取反操作:即红队变为蓝队,蓝队变成红队。
- 操作2:把某一列的同学进行取反操作:即红队变为蓝队,蓝队变成红队。
小A想知道,有多少种方案满足:执行完所有的操作1和操作2之后,蓝队同学恰好有t名。
由于答案可能会很大,所以只需要输出结果对555555555取模的结果即可。
PS:若两个方案中的某一行或列被小A进行操作的次数不同时,则视为两种不同的方案
题解
这是一道组合数学的题目,首先考虑一下如果有i行和j列被翻转,然后就有$i∗m+j∗n−2∗i∗j$个格子变成蓝色的,并且$i∗m+j∗n−2∗i∗j=t$,
且(p-i)和(q-j)都是偶数的(因为剩下的都是要操作偶数次抵消的)话是合法的,(p-i)和(q-j)是多余的操作,而多余的操作想要抵消就必须是偶数个才行。
然后我们考虑这个贡献怎么算?
很显然我们可以从n中任意选i个翻转,C[n][i]
我们也可以从m中任意选j个翻转,C[m][j]
然后对于多余的操作p-i,我一开始想的是因为p-i一定是偶数,可以抵消,那么我们分配(p-i)/2就行,另一半部分跟着之前操作就行.
这个就相当于现在我们有(p-i)/2个操作要执行,每个操作可以选任意一行,且与顺序无关,那问题转换一下,有(p-i)/2个球,现在有n个盒子,
现在要把球放在盒子里,随机放,方案是多少?
我们利用隔板法来做:现在有(p-i)/2+n个球,有n个盒子,每个盒子至少一个球,所有球排成一列,有(p-i)/2+n-1个间隙,在这些间隙中插入n-1个隔板,这样就可以分出n个空间,相当于n个盒子,也就是C[(p−i)/2+n−1][n−1]
Code
#include<iostream> #include<algorithm> using namespace std; typedef long long ll; const int maxn=3510; const int mod=555555555;//组合 ll n,m,p,q,t; ll C[maxn][maxn]; void get_C() { C[0][0] = 1; for(int i=1;i<3510;i++){ C[i][0] = 1; for(int j=1;j<=i;j++) C[i][j] = (C[i-1][j]+C[i-1][j-1])%mod; } } void inint(){ cin>>n>>m>>p>>q>>t; } ll res=0; ll mmin(ll aa,ll bb){ if(aa>bb){ return bb; } return aa; } int main(){ inint(); get_C(); for(int i=0;i<=mmin(n,p);i++){ for(int j=0;j<=mmin(m,q);j++){ ll a=p-i,b=q-j,s=i*m+j*n-2*i*j; if(s!=t||a&1||b&1){ continue; } a/=2,b/=2; ll r=C[n][i],c=C[m][j]; if(n-i-1>=0){ r=(r*C[a+n-1][n-1])%mod; } if(m-j-1>=0){ c=(c*C[b+m-1][m-1])%mod; } res=(res+r*c%mod); res%=mod; } } cout<<res<<"\n"; }