分配颜色

题目描述

传送门

今天小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$个格子变成蓝色的,并且$im+jn2ij=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";
}

 

 

posted @ 2021-10-27 19:43  lipu123  阅读(244)  评论(0)    收藏  举报