[BZOJ2839] 集合计数

[BZOJ2839] 集合计数

Description

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

Input

一行两个整数N,K

Output

一行为答案。

Sample Input

3 2

Sample Output

6

HINT

【样例说明】假设原集合为{A,B,C}则满足条件的方案为:{AB,ABC},{AC,ABC},{BC,ABC},{AB},{AC},{BC}【数据说明】 对于100%的数据,1≤N≤1000000;0≤K≤N;

试题分析

依旧不能保证取出来一定是严格K个,所以设\(f_k\)为交集大小至少为k的方案数。
那么\(f_k=\binom{n}{k} (2^{2^{n-k}} -1)\)
上面的\(2^{n-k}\)代表集合数量,也就是要去掉k个剩下的集合数量,这些集合一定包含交集。
然后还要将这些集合挑选若干个选出来,最后-1是一个集合都没有选的。
那么设容斥系数为\(g_k\),有\(ans=\sum_{i=0}^K g_i\times f_i\)
于是有:$$[xm]=\sum_{i=0}^K f_i \times \binom{n}{i}$$
根据二项式反演:$$f(n) = \sum_{i = 0}^{n}\binom{n}{i}g(i) \Leftrightarrow g(n) = \sum_{i = 0}^{n} (-1)^{n - i} \binom{n}{i} f(i)$$
有:$$f(x)=\sum_{i=0}^x (-1)^{x-i} \binom {x}{i} [i
m]$$
带回到原式中即可。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<algorithm>
 
using namespace std;
#define LL long long
 
inline LL read(){
    LL x=0,f=1; char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*f;
}
const LL INF = 2147483600;
const LL MAXN = 2000010;
const LL Mod = 1000000007;
 
LL N,M; LL inv[MAXN+1],fac[MAXN+1],ifac[MAXN+1],Pow[MAXN+1];
inline LL Powl(LL A,LL B){
    LL res=1LL; for(; B ; B>>=1,A=A*A%Mod) if(B&1) res=res*A%Mod; return res;
}
inline void init(){
    inv[1]=1; fac[1]=1; ifac[1]=1; 
    for(LL i=2;i<=N;i++) fac[i]=fac[i-1]*i%Mod;
    for(LL i=2;i<=N;i++) inv[i]=(Mod-(Mod/i)*inv[Mod%i])%Mod;
    for(LL i=2;i<=N;i++) ifac[i]=ifac[i-1]*inv[i]%Mod;
    Pow[0]=1; for(LL i=1;i<=N;i++) Pow[i]=Pow[i-1]*2LL%(Mod-1);
    return ;
}
inline LL C(LL n,LL m){
    if(n==m) return 1; if(!m) return 1;
    //printf("ifac[%lld] = %lld   ifac[%lld] = %lld   fac[%lld] = %lld\n",n-m,ifac[n-m],m,ifac[m],n,fac[n]);
    return fac[n]*ifac[m]%Mod*ifac[n-m]%Mod;
}
LL ans=0;
 
int main(){
    //freopen(".in","r",stdin);
    //freopen(".out","w",stdout);
    N=read(),M=read(); init();
    for(LL i=M;i<=N;i++){
        LL ret=((i-M)&1)?-1:1;
        //cout<<C(i,M)<<" "<<C(N,i)<<" "<<Pow[N-i]<<endl;
        ret=ret*C(i,M)%Mod*C(N,i)%Mod*(Powl(2LL,Pow[N-i])-1)%Mod;
        ret=(ret%Mod+Mod)%Mod; (ans+=ret)%=Mod;
    } printf("%lld\n",ans);
    return 0;
}
posted @ 2018-08-29 21:36  wxjor  阅读(457)  评论(0编辑  收藏  举报