容斥原理

题目链接: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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值