[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} [im]$$
带回到原式中即可。
#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;
}
你——悟到了么?