参考了两篇博客,并摘抄了里面的部分解释
此题目是用的容斥原理,设第一行没有石子的方法数为A,最后一行没有石子的方法数为B,第一列没有石子的方法数为C,最后一列没有石子的方法数为D,不加任何限制而放置k个石子的总方法数为S。那么答案应该为S - (A U B U C U D)。用二进制的方式表示集合A、B、C、D的组合方式。0001表示在A中、0010表示在B中、0100表示在C中、1000表示在D中、0101表示在A和C中、0111表示在A、B和C中。
所求的方案就是在S中但不在ABCD中任何一个的方案即:S - |A∪B∪C∪D|
而|A∪B∪C∪D| = |A| + |B| + |C| + |D| - |A∩B| - |A∩C| - |A∩D| - |B∩C| - |B∩D| - |C∩D|
+ |A∩B∩C| + |A∩B∩D| + |A∩C∩D| + |B∩C∩D| - |A∩B∩C∩D|
所以b为奇数的时候做减法,而偶数的时候做加法。以前看过与运算的解释,可能没怎么理解二进制吧,现在又忘了,所以再记一下吧。
1(十进制) = 1(二进制)
2(十进制) = 10(二进制)
4(十进制) = 100(二进制)
8(十进制) = 1000(二进制)
if(S&1),代表S转化为二进制的最后一位是1,所以十进制S是个奇数
if(S&2),代表S转化为二进制的倒数第二位是1
if(S&4),代表S转化为二进制的倒数第三位是1
……
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int Mod = 1000007;
const int N = 505;
int C[N][N];
int main()
{
memset(C, 0, sizeof(C));
//打表组合数
C[0][0] = 1;
for(int i = 0; i < N; i++)
{
C[i][0] = C[i][i] = 1;
for(int j = 1; j < i; j++)
C[i][j] = (C[i-1][j] + C[i-1][j-1]) % Mod;
}
int t;
scanf("%d",&t);
for(int cas = 1;cas <= t;cas++)
{
int n,m,k,sum = 0;
scanf("%d%d%d",&n,&m,&k);
for(int S = 0; S < 16; S++)// 枚举所有16种“搭配方式”
{
int b = 0,r = n,c = m;
if(S&1) { r--; b++;}
if(S&2) { r--; b++;}
if(S&4) { c--; b++;}
if(S&8) { c--; b++;}
if(b&1)//奇数个条件
sum = (sum + Mod - C[r*c][k]) % Mod;
else//偶数个条件
sum = (sum + C[r*c][k]) % Mod;
}
printf("Case %d: %d\n",cas,sum);
}
return 0;
}