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;
}