2839: 集合计数
Time Limit: 10 Sec Memory Limit: 128 MB
Submit: 243 Solved: 129
Description
一个有N个元素的集合有2^N个不同子集(包含空集),现在要在这2^N个集合中取出若干集合(至少一个),使得
它们的交集的元素个数为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;
容斥原理
类似bzoj3198的思路,f[i]表示交集的个数至少是i个的方案数,最终答案ans=∑f[i]*C(i,k)*(-1)^(i-k),k≤i≤n。
于是问题转化为如何求f[i]。
加入限制了交集必须有i个数,等于将可选的集合范围缩小到了2^(n-i)个。而每一个集合都是可选可不选的,但是不能全部不选,所以f[i]=2^[2^(n-i)]-1。
可以发现随着i递减,2^[2^(n-i)]每一次都是上一次的平方,然后倒着算就好了。
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<cstring> #include<algorithm> #define F(i,j,n) for(int i=j;i<=n;i++) #define D(i,j,n) for(int i=j;i>=n;i--) #define ll long long #define maxn 1000005 #define mod 1000000007 using namespace std; int n,k; ll ans,fac[maxn],inv[maxn]; inline int read() { int x=0,f=1;char ch=getchar(); while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();} while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } inline ll getpow(ll x,ll y) { ll ret=1; for(;y;y>>=1,x=x*x%mod) if (y&1) ret=ret*x%mod; return ret; } int main() { n=read();k=read(); fac[0]=1; F(i,1,n) fac[i]=fac[i-1]*i%mod; inv[0]=1;inv[1]=1; F(i,2,n) inv[i]=inv[i-mod%i]*(mod/i+1)%mod; F(i,2,n) inv[i]=inv[i]*inv[i-1]%mod; ll x=2; D(i,n,k) { if (i!=n) x=x*x%mod; ll tmp=fac[n]*inv[n-i]%mod*inv[k]%mod*inv[i-k]%mod; ans=(ans+tmp*(x+mod-1)%mod*((i-k)&1?mod-1:1)%mod)%mod; } printf("%lld\n",ans); return 0; }
时间: 2024-10-25 21:07:39