bzoj2839集合计数 容斥原理

11 篇文章 0 订阅
6 篇文章 0 订阅

题意:一个有N个元素的集合有2^N个不同子集(包含空集),现在要在这2^N个集合中取出若干集合(至少一个),使得
它们的交集的元素个数为K,求取法的方案数,答案模1000000007。

明显容斥啦。
应该是选择k个-选择k+1个+选择k+2…..
那么我们设f[i]表示已经选择k个作为交集,剩下n-k个如何选择的方案。
那么明显有f[i]=2^{2^(n-i)}-1,由于已经选择了k个,剩下n-k个集合可以选或者不选,但是不能全部不选所以-1。
那么剩下n-k如何选择已经求出,问题就是前面k个了,方案为C(n,i)*C(i,k)(k<=i<=n).
上面那个也好理解,具体来说,就是我现在只有可能选择i个,在这i个里面选择k个作为交集的方案。
注意求f的时候,幂次的mo是1e9+7-1,费马小定理。

#include<cstdio>
#include<algorithm>
#include<cstring>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int N=1e6+5;
const int mo=1e9+7;
typedef long long ll;
ll fac[N],inv[N];
ll f[N],g[N];
int n,k; 
inline ll C(int n,int m)
{
    return fac[n]*inv[m]%mo*inv[n-m]%mo;
}
inline ll pow(ll a,ll b,ll mo)
{
    ll ret=1;
    while(b)
    {
        if (b&1)ret=ret*a%mo;
        a=a*a%mo;
        b>>=1;
    }
    return ret;
}
int main()
{
    scanf("%d%d",&n,&k);
    fac[0]=1;
    fo(i,1,n)fac[i]=fac[i-1]*i%mo;
    inv[0]=1;
    fo(i,0,n)inv[i]=pow(fac[i],mo-2,mo)%mo;
    g[n]=2,f[n]=1;
    fd(i,n-1,0)
    {
        g[i]=pow(2,pow(2,n-i,mo-1),mo);
        f[i]=(g[i]-1)%mo;
    }
    ll ans=0,c=-1;
    fo(i,k,n)
    {
        c*=-1;
        if (c<0)
        ans=(ans-f[i]*C(i,k)%mo*C(n,i)%mo+mo)%mo;
        else ans=(ans+f[i]*C(i,k)%mo*C(n,i)%mo)%mo;
    }
    printf("%lld\n",(ans+mo)%mo);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值