标签:
题意:
给出M,R,G,B,2M=R+B+G,M代表一个2*M区域的列数,RGB分别代码红绿蓝格子的个数,要求:每个2*2格子中,三种颜色每种至少有一个格子,且相邻格子颜色不同,问有多少种排列方式
首先我们发现可以把一列两格的放置方案分为两组,{RB,BG,GR},{BR,GB,RG}
一个合法方案必定只由其中的一组组成,因此我们可以计算一组的方案,然后将其×2即可
那么我们假设RB有x个,BG有y个,GR有z个,那么ans等价于:求使得BR,RG,GB分别不能与自己相邻的放置方案数。
我们假设x>y>z,首先我们要用 y,z去填补x之间的空缺,
如果y+z<x-1,则无法填补,返回0
否则,我们有4种方案,填中间x-1个,左边x个,右边x个,全部x+1个,我们对于x-1~x+1分别计算即可
那么现在有nx(nx=x+1/x/x-1)个空,我们要用y,z去填,
1.首先我们假设用y填y1个空,方案为C(nx,y-1),用z填z1=x-y1个空
2.设多出的y2=y-y1,把y2塞进去的方案等价于把所有的y分成 y1份的方案数,为C(y-1,y1-1)
对于每个多塞的y,我们要塞一个z来防止y相邻,那么塞完y后我们剩的z为z2=z-z1-y2,再将这些z分别插个y1个序列的两端,方案为C(2*y1,z2)
至此,分配结束
#include<cstdio> #include<cstdlib> #include<algorithm> #include<cmath> #include<cstring> using namespace std; typedef long long ll; int N=1000000; int mo=1000000007; int jc[1000011],fc[1000011]; int ans,tmp,ts,x,y,z,xl,yl,req,zl,dt,tj,M,R,G,B,j,i; int mi(int x,int z) { int l; l=1; while(z){ if(z%2==1)l=(ll)l*x%mo; x=(ll)x*x%mo; z/=2; } return l; } int C(int n,int m) { return (ll)jc[n]*fc[m]%mo*fc[n-m]%mo; } int main() { jc[0]=1; for(i=1;i<=N;i++)jc[i]=(ll)jc[i-1]*i%mo; fc[N]=mi(jc[N],mo-2); for(i=N-1;i>=0;i--)fc[i]=(ll)fc[i+1]*(i+1)%mo; scanf("%d",&dt); for(tj=1;tj<=dt;tj++){ scanf("%d%d%d%d",&M,&R,&G,&B); x=M-B;y=M-R;z=M-G; if(x<0||y<0||z<0){ printf("0\n"); continue; } ans=0; if(y>x)swap(x,y); if(z>x)swap(x,z); if(z>y)swap(y,z); if(y+z<x-1){ printf("0\n"); continue; } for(xl=x-1;xl<=x+1;xl++){ if(y+z<xl)continue; tmp=0; for(j=1;j<=y;j++){ req=xl-j; if(req>z)continue; yl=y-j; if(req+yl>z)continue; ts=C(xl,j); ts=(ll)ts*C(y-1,j-1)%mo; zl=z-req-yl; if(zl>2*j)continue; ts=(ll)ts*C(2*j,zl)%mo; tmp=(tmp+ts)%mo; } if(xl==x)tmp=tmp*2%mo; ans=(ans+tmp)%mo; } ans=(ans*2)%mo; printf("%d\n",ans); } }
标签:
原文地址:http://www.cnblogs.com/applejxt/p/4584852.html