Problem Statement |
|||||||||||||
|
Little Elephant from the Zoo of Lviv has a board with 2 rows and M columns. Each cell of the board must be painted in one of three colors: red, green, or blue. The board is called magical if and only if it has the following properties:
You are given four ints M, R, G andB. Let X be the total number of different magical boards with 2 rows andM columns that contain exactlyR red cells, G green cells, and B blue cells. Return the value (X modulo 1,000,000,007). |
||||||||||||
Definition |
|||||||||||||
|
|
||||||||||||
|
|||||||||||||
|
|||||||||||||
Constraints |
|||||||||||||
- |
M will be between 2 and 1,000,000, inclusive. |
||||||||||||
- |
R, G and B will each be between 0 and 1,000,000, inclusive. |
||||||||||||
- |
R+G+B will be equal to 2M. |
||||||||||||
Examples |
|||||||||||||
0) |
|
||||||||||||
|
|
||||||||||||
1) |
|
||||||||||||
|
|
||||||||||||
2) |
|
||||||||||||
|
|
||||||||||||
3) |
|
||||||||||||
|
|
类型:数论 难度:3.5
题意:给出M,R,G,B,2M=R+B+G,M代表一个2*M区域的列数,RGB分别代码红绿蓝格子的个数,要求:每个2*2格子中,三种颜色每种至少有一个格子,且相邻格子颜色不同,问有多少种排列方式
分析:比较复杂的一道数学问题。纠结了很多天。。
先将问题转化:
由于要满足上述要求,观察可得,每列的格子有6种状态,(RG,GB,BR),(RB,BG,GR),前三个看成一组,后三个看成一组,每组的三个状态可以互相转化,但是同种状态不相邻
可见第二组实际上是第一组每个情况上下互换位置所得,所以两组计算出的排列数相同,算出其中一种*2即可。
设第一组每个状态的个数为x,y,z,可得:x=M-B, y=M-R, z=M-G
那么问题转化为:给出三种颜色的球,个数为x,y,z,将其排成一行,要求同颜色球不相邻,问有多少种排列方式
考虑用插空法解决问题,设x>=y>=z,解法如下:
1、若y+z<x-1,返回0。即yz加起来都无法填补x个球的空隙,没有合法情况。
2、设yz所填的x的空隙数目为x1,x1=x-1,x,x+1,因为左右两边的空隙可放可不放,所以实际是四种情况,填左边x个空,右边x个空,中间x-1个空,填满x+1个空
遍历x1,累加结果,x1=x时,由于是两种情况,累加两次
3、在x1个位置中,设有y的位置为y1,y1<=y,那么先要从x1中选y1个位置,数量为c(x1,y1)
4、然后,还剩x1-y1个位置,这些位置每个位置都要放1个z,设为z1=x1-y1
5、那么,剩余的y(多出来的y)是y2=y-y1,将y2个y放到y1个位置上的数目是c(y1+y2-1,y1-1)(可以看成一个长度为y2+y1-1的序列,挑出y1-1个分割点)剩余的z是z2=z-z1,填充这些y的空隙需要y2个z,那么最后剩余的z就是z3=z2-y2
6、这z3个z能够放入的位置是每个yzyz...zy序列的两侧,这样的序列有y1个,那么z3个z的放法就是c(2y1,z3),至此,所有球分配完毕
由于M,R,G,B的范围为10^6,要在O(1)计算出c(m,n)%mod必须使用数组记录辅助数据,这段代码是参照别人的,就不细讲了。。
代码:
#include<string> #include<cstdio> #include<vector> #include<cstring> #include<map> #include<iostream> #include<algorithm> #include<cmath> #include<set> using namespace std; #define SWAP(x,y,t) (t)=(x),(x)=(y),(y)=(t) const int mod = 1000000007; const int maxn = 1000011; int f[maxn],df[maxn],inv[maxn]; int multi(int x,int y) { int tmp; if(x<y) SWAP(x,y,tmp); if(y==0) return 0; if(y==1) return x%mod; int half = multi(x,y/2)%mod; int ans = (half+half)%mod; if(y%2==0) return ans; else return (ans+x)%mod; } int mc(int a,int b) { int tmp; if(b>a) SWAP(a,b,tmp); if(b<0) return 0; return (((long long)f[a]*df[b])%mod * df[a-b])%mod; } class LittleElephantAndBoard { public: int getNumber(int M, int R, int G, int B) { if(R>M || R<M/2 || G>M || G<M/2 || B>M || B<M/2) return 0; inv[1] = 1; for(int i=2; i<maxn; i++) inv[i] = ((long long)inv[mod%i] * (mod-mod/i))%mod; f[0] = df[0] = 1; for(int i=1; i<=maxn; i++) { f[i] = ((long long)f[i-1]*i)%mod; df[i] = ((long long)df[i-1]*inv[i])%mod; } int x,y,z,tmp; x = (G+R-B)/2; y = (G-R+B)/2; z = (R-G+B)/2; if(x<y) SWAP(x,y,tmp); if(x<z) SWAP(x,z,tmp); if(y<z) SWAP(y,z,tmp); int ans = 0; for(int i=-1; i<=1; i++) { int x1,y1,z1,ansx; x1 = x+i; y1 = y; if(x1==0) continue; if(x1<y1) y1=x1; ansx = 0; for(; ; y1--) { int y2,z2; z1 = x1-y1; y2 = y-y1; z2 = z-z1; if(y2>z2) break; int t1,t2,t3; t1 = mc(x1,y1)%mod; t2 = mc(y1+y2-1,y1-1)%mod; t3 = mc((y1+y1)%mod,z2-y2)%mod; t1 = multi(t1,t2)%mod; t1 = multi(t1,t3)%mod; ansx = (ansx + t1) % mod; //printf("x1 %lld y1 %lld z1 %lld y2 %lld z2 %lld t1 %lld t2 %lld t3 %lld\n", // x1,y1,z1,y2,z2,t1,t2,t3); } ans = (ans+ansx)%mod; if(i==0) ans = (ans+ansx)%mod; ans %= mod; } ans = (ans+ans)%mod; return (int)ans; } }; int main() { //cout<<mc(1000000,500)<<endl; LittleElephantAndBoard t; cout<<t.getNumber(474, 250, 300, 398)<<endl; //cout<<t.getNumber(10,7,7,6)<<endl; //cout<<t.getNumber(3,1,2,3)<<endl; }