bzoj 2839: 集合计数 (容斥原理)

来源:互联网 发布:天刀公子羽捏脸数据 编辑:程序博客网 时间:2024/04/29 04:32

题目描述

传送门

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

题解

首先我们 考虑确认出交集中的K个元素,选择的方案数是C(n,k).那么剩下的元素共nk个,剩下的元素构成的子集是2nk我们可以从这些子集中任意选择,然后与选择出的元素拼接到一起形成合法的子集。选择的方案就是2nki=1C(2nk,i) 发下这个式子就是杨辉三角的第2nk+1行减去C(2nk,0),有公式可知,杨辉三角的第i行的和为2i1,所以选择的方案就是22nk1,由欧拉定理aϕ(n)=1mod n,所以我们可以利用快速幂求解。
但是我们选取出的子集的交集至少为k的,那么需要容斥原理来计算。根据找规律的容斥的系数是C(i,k).

代码

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<cmath>#define p 1000000007#define LL long long #define N 1000003using namespace std;LL jc[N],inv[N],mi[N*3];int n,k;LL quickpow(LL num,int x,LL p1){    if (x<=3000000&&p1==p) return mi[x];    LL base=num%p; LL ans=1;    while (x) {        if (x&1) ans=ans*base%p1;        x>>=1;        base=base*base%p1;    }    return ans;}LL C(int n,int m){    return jc[n]*inv[m]%p*inv[n-m]%p;}int main(){    scanf("%d%d",&n,&k);    jc[0]=1; mi[0]=1;    for (int i=1;i<=n;i++) jc[i]=jc[i-1]*i%p;    for (int i=0;i<=n;i++) inv[i]=quickpow(jc[i],p-2,p);    for (int i=1;i<=3000000;i++) mi[i]=mi[i-1]*2%p;    LL ans=0; int t;    for (int i=k;i<=n;i++) {      if ((i-k)&1) t=-1;      else t=1;      LL cnt=quickpow(2,n-i,p-1);      ans+=C(n,i)*C(i,k)%p*(quickpow(2,cnt,p)-1)%p*t;      ans%=p;         //cout<<ans<<endl;    }    printf("%lld\n",(ans%p+p)%p);}
0 0
原创粉丝点击