题目链接:https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2906
本题大致意思是讲:给定一个广场,把它分为M行N列的正方形小框。现在给定有K个拉拉队员,每一个拉拉队员需要站在小框内进行表演。但是表演过程中有如下要求:
(1)每一个小框只能站立一个拉拉队员;
(2)广场的第一行,最后一行,第一列,最后一列都至少站有一个拉拉队员;
(3)站在广场的四个角落的拉拉队员可以认为是同时占据了一行和一列。
借鉴别人的容斥原理证明过程:
这个题目直接计算不大好算,但是反过来去计算不符合要求的局面则容易很多。
我们不妨设A[i]表示我们规定其中i条边上不能放,而其他的地方随便放,所得的一个结果(比如i=2,我们要枚举是哪2条边没有放,然后其他地方随便放,可以用组合数求得结果,最后将每次枚举所得的结果加起来作为A[2]的值)。再设a[i]表示有且仅有i条边上没有放的情况数(比如i=2,同样是枚举哪2条边没有放,最后C(4,2)种枚举所得的情况之和才是a[2]的值)。
我们想要的显然是a[1]+a[2]+a[3]+a[4],这就是所有不符合要求的情况,但是同样不好直接计算,但是A[i]却很好计算,我们只要枚举哪些边没有放,然后再用组合数计算其他地方随便放的方案数即可。我们列出通过上述规则计算所得的A[i]由哪些部分组成,比如A[1],我们按上述规则计算之后会发现a[1]算了1次,a[2]算了2次,a[3]算了3次,a[4]算了4次,也就是A[1]=a[1]+2*a[2]+3*a[3]+4*a[4]。同样的,可以得到A[2]=a[2]+3*a[3]+6*a[4],A[3]=a[3]+4*a[4],A[4]=a[4]。
这样根据上面计算所得的A[1]、A[2]、A[3]、A[4]可以得到a[1]+a[2]+a[3]+a[4]=A[1]-A[2]+A[3]-A[4],这样就能得到我们所要的结果了。
结论
容斥原理满足:奇减偶加;
代码:
#include <iostream>
#include <cstring>
using namespace std;
const int mod=1000007;
int a[500+10][500+10];
int main()
{
memset(a,0,sizeof(a));
a[0][0]=1;
for(int i=0;i<=500;i++)
{
a[i][0]=a[i][i]=1;
for(int j=1;j<i;j++)
a[i][j]=(a[i-1][j]+a[i-1][j-1])%mod;
}
int t;
cin>>t;
int flag=0;
while(t--)
{
int n,m,k;
cin>>n>>m>>k;
int sum=0;
if(n*m<k)
{
cout<<"Case "<<++flag<<": "<<0<<endl;
continue;
}
for(int i=0;i<16;i++)
{
int r=n,c=m,b=0;
if(i&1)
{
r--;
b++;
}
if(i&2)
{
r--;
b++;
}
if(i&4)
{
c--;
b++;
}
if(i&8)
{
c--;
b++;
}
if(b&1)
{
sum=(sum+mod-a[c*r][k])%mod;
}
else
{
sum=(sum+a[r*c][k])%mod;
}
}
cout<<"Case "<<++flag<<": "<<sum%mod<<endl;
}
return 0;
}