Time Limit: 10 Sec Memory Limit: 128 MB
Submit: 243 Solved: 129
[Submit][Status][Discuss]
【样例说明】
假设原集合为{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;
}