一个不大不小的日子——我终于注册了
UVA
这么个举世文明的网站,并且答对了几道题。(但是为什么UVA
的注册信息会跑到
UVA11806 Cheerleaders \color{green}{\texttt{UVA11806 Cheerleaders}} UVA11806 Cheerleaders
[Problem] \color{blue}{\texttt{[Problem]}} [Problem]
在一个 n × m n \times m n×m 的矩形网格里放 k k k 个相同的石子,问有多少种合格的摆法。一共 T T T 组输入数据。
一个合格的摆法应该满足一下条件:
- 每个格子里最多有 1 1 1 个石子。
- 所有的石子都要用完。
- 网格第一行、第一列、最后一行、最后一列都要有石子。
1 ≤ T ≤ 50 , 2 ≤ n , m ≤ 20 , 0 ≤ k ≤ 500 1\leq T \leq 50,2 \leq n,m \leq 20,0 \leq k \leq 500 1≤T≤50,2≤n,m≤20,0≤k≤500。
翻译改自 《算法竞赛入门经典》
一书。
[Solution] \color{blue}{\texttt{[Solution]}} [Solution]
我们发现直接求有点难,怎么办呢?正难则反,我们考虑容斥。
记满足“第一行没有石子”的方案集为 A A A,“第一列没有石子”的方案集为 B B B,“最后一行没有石子”的方案集为 C C C,“最后一列没有石子”的方案集为 D D D,全集为 S S S,则总的答案即为“在 S S S 但不在 A , B , C , D A,B,C,D A,B,C,D 中任何一个集合中的元素”的个数。
把第一行、最后一行、第一列、最后一列有无石子的状态存入一个二进制中( 1 1 1 表示没有, 0 0 0 表示有),总的状态可以通过枚举得到。
考虑对于一个状态如何求其方案数。我们发现,如果第一行或者最后一行没有石子的话,我们可以把那一行删除,即只剩下 n − 1 n-1 n−1 个有用行(或者叫备选行)。对于列同理,注意当第一行和最后一行同时没有石子时,就只剩下 n − 2 n-2 n−2 个有用行了。
假设有 r r r 个有用行, c c c 个有用列,则总的方案数相当于是在 r × c r\times c r×c 这么多个格子选 k k k 个格子的方案数,即:
( r × c k ) = C r × c k \binom {r \times c}{k}=C^{k}_{r \times c} (kr×c)=Cr×ck
[code] \color{blue}{\texttt{[code]}} [code]
const int mod=1e6+7;//模数
int test_number,C[510][510];
int main(){
scanf("%d",&test_number);
for(int i=0;i<=500;i++){
C[i][i]=C[i][0]=1;
for(int j=1;j<i;j++)
C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
}
for(int T=1;T<=test_number;T++){
register int n,m,k,sum=0;
scanf("%d%d%d",&n,&m,&k);
for(int S=0,r,c,b;S<16;S++){
r=n;c=m;b=0;//初始化变量
if (S&1){r--;b++;}//第1行
if (S&2){r--;b++;}//第n行
if (S&4){c--;b++;}//第1列
if (S&8){c--;b++;}//第m列
if (b&1) sum=(sum-C[r*c][k]+mod)%mod;
else sum=(sum+C[r*c][k])%mod;
}
printf("Case %d: %d\n",T,sum);
}
return 0;
}
容斥和正难则反的思维在实际中的应用是非常广的,大家一定要注意,平时遇到一道直接计算很棘手的题目时,就可以想象可不可以用这两种方法。