棋盘

来源:互联网 发布:淘宝iphone官换机 编辑:程序博客网 时间:2024/04/27 14:39

棋盘

题目描述

给你一个R行C列的棋盘,每个格子可以染成黑色或白色。如果一个染色方案使棋盘的某一行或某一列全为白色,那么这个染色方案使非法的,否则是合法的。
请问:有多少种合法的染色方案?由于答案可能非常大,请你输出答案对1,000,000,007取模后的结果。

【约定】
30%的数据满足,1≤R, C≤4;
100%的数据满足,1≤R, C≤2000。

输入格式 2161.in

一行两个整数R, C

输出格式 2161.out

合法的染色方案数对1,000,000,007取模后的结果。

输入样例 2161.in

【输入样例1
2 2
【输入样例2】
3 3
【输入样例3】
123 321

输出样例 2161.out

【输出样例1
7
【输出样例2】
265
【输出样例3】
383377374

       此题用容斥原理做,有两种办法。

方法一:行、列同时容斥

     直接求合法方案数比较难。不妨换个思路: 用全部方案数减去非法方案数。

     全部方案数自然为2^(n*m)。

     至于非法方案,就要用到容斥原理了。我们可以规定好空的行列,其余的格子不做规定,就是某些情况的非法方案数了。

     1.可能有某一行是空的,于是减去2^[(n-1)*m]*c[n][1]

     2.可能有某一列是空的,于是减去2^[(m-1)*n]*c[m][1]

     3.可能有一行,一列是空的,这样的情况在之前进行了重复的计算,减多了。于是加上c[n][1]*c[m][1]*{2^[(m-1)*(n-1)]} 

     4.可能有一行,两列是空的。在算3.的时候,加多了。因此应减去c[n][1]*c[m][2]*{2^[(m-2)*(n-1)]}

……

    通过分析不难发现,假设我们枚举有i行,j列为空,那么,当i+j为奇数时,应加上方案数;否则减去方案数。

    最后答案即为所求。

    代码如下:

#include #include typedef long long LL;#define Mod 1000000007LL C[2050][2050], pow2[4000050];int main(){int N, M;scanf("%d%d", &N, &M);C[0][0] = 1;for (int i = 1; i <= N || i <= M; ++ i){C[i][0] = 1;for (int j = 1; j <= i; ++ j) C[i][j] = (C[i-1][j-1] + C[i-1][j]) % Mod;}pow2[0] = 1;for (int i = 1; i <= N*M; ++ i) pow2[i] = pow2[i-1] * 2 % Mod;LL Ans = 0;for (int i = 0; i <= N; ++ i) for (int j = 0; j <= M; ++ j)Ans = (Ans + (((i+j)&1)*(-2)+1) * pow2[(N-i)*(M-j)] % Mod * C[N][i] % Mod * C[M][j] % Mod) % Mod;printf("%d\n", int( (Ans + Mod) % Mod ));return 0;}

方法二、只对列进行容斥

    假设本题只有行的限制,运用排列组合的知识,不难求得方案数为(2^m-1)^n。

    现在增加了一个列的限制,需要两个条件都要保证。其实,就是上述问题的一个变种。

    现在同样的思路,合法方案数=总方案数-非法方案数

    既然列不能为空,那我就将空的列枚举出来,在此基础上,规定每行都不能空。

    假设有1列为空,那么这些是非法方案,因此ans-=c[m][1]*{[2^(m-1)-1]^n}

    假设有2列为空,这些方案在算1列为空时重复了,因此ans+=c[m][2]* {[2^(m-2)-1]^n}

    ……

    以此类推,我们枚举空的列数为j,c[m][j]*{[2^(m-j)-1]^n}种方案。j为奇数时,加上它;否则减去它即可。

    代码如下:

#include#includeusing namespace std;const int MAXN=2005;long long n,m;long long c[MAXN][MAXN];long long mod=1e9+7,ans,a;void C(){for(int i=0;i<=m;i++){for(int j=0;j<=i;j++){if(j==0||j==i)c[i][j]=1;elsec[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;}}}long long g(long long m){long long a=1,b;for(int i=1;i<=m;i++)a=(a*2)%mod;a=(a+mod-1)%mod;b=1;for(int i=1;i<=n;i++)b=(b*a)%mod;return b;}long long f(int n,int m){long long num=0;for(int i=1;i<=m;i++){if(i%2==1)num=(num+c[m][i]*g(m-i)%mod)%mod;elsenum=(num+mod-c[m][i]*g(m-i)%mod)%mod;}return num;}int main(){cin>>n>>m;C();ans=g(m);ans=(ans+mod-f(n,m))%mod;cout<

          题,可以说是转化思想的成功典范。合法方案=总方案-非法方案的思想 普遍应用于题目中!

    本题,让我更深一点地了解了容斥原理。在计算某些方案时,先不要给它太多的限制,先不考虑可能重复的情况。反正,多加了的,可以减回来;多减了的,可以加回来。弄清楚几点关键的:计算方案数时,给了哪些限制;什么与什么会造成重复;在什么情况下要减、加。将问题想清楚,方法、答案就显而易见了。



原创粉丝点击