[BZOJ2839]集合记数 容斥原理

BZOJ2839
  • 一个有N个元素的集合有 2 N 2^{N} 2N个不同子集(包含空集),现在要在这 2 N 2^N 2N个集合中取出若干集合(至少一个),使得它们的交集的元素个数为 K K K,求取法的方案数,答案模1000000007。
  • 交集元素个数至少为 K K K的方案数好求,恰好元素个数为 K K K的方案数不好求。
  • f [ k ] f[k] f[k]表示至少为 K K K的方案数, g [ k ] g[k] g[k]表示恰好为 K K K的方案数。
  • f [ k ] = ∑ i = k n C i k ∗ g [ i ] f[k]=\sum_{i=k}^{n}C_{i}^{k}*g[i] f[k]=i=knCikg[i]。二项式反演一下, g [ k ] = ∑ i = k n ( − 1 ) i − k C i k ∗ f [ i ] g[k]=\sum_{i=k}^{n}(-1)^{i-k}C_{i}^{k}*f[i] g[k]=i=kn(1)ikCikf[i]
  • 现在只需要考虑 f f f怎么求。 f [ k ] = C n k ∗ ( 2 2 n − k − 1 ) f[k]=C_{n}^{k}*(2^{2^{n-k}}-1) f[k]=Cnk(22nk1)
  • 2 2 n − k 2^{2^{n-k}} 22nk不好直接求,根据欧拉定理的推论,把幂先模 φ ( m o d ) \varphi(mod) φ(mod) m o d − 1 mod-1 mod1
  • O ( n ) O(n) O(n)预处理一下 f f f数组, O ( n ) O(n) O(n)求答案。
Coding
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int N=1e6+10;
const int mod=1e9+7;
int n,k;
ll inv[N],jie[N],njie[N],f[N],ans;
ll power(ll a,ll b,ll p){
	ll res=1%p;
	for(;b;b>>=1){
		if(b&1) res=res*a%p;
		a=a*a%p;
	}
	return res;
}
int main(){
	scanf("%d%d",&n,&k);inv[1]=1;
	for(int i=2;i<=n;++i){
		ll x=-mod/i*inv[mod%i]%mod;
		x=(x+mod)%mod;inv[i]=x;
	}jie[1]=njie[1]=1;jie[0]=njie[0]=1;
	for(int i=2;i<=n;++i) jie[i]=jie[i-1]*i%mod,njie[i]=njie[i-1]*inv[i]%mod;
	for(int i=0;i<=n;++i){
		ll c=jie[n]*njie[i]%mod*njie[n-i]%mod;
		ll temp=power(2,n-i,mod-1);
		f[i]=c*(power(2,temp,mod)-1)%mod;
		f[i]=(f[i]+mod)%mod;
	}
	for(int i=k;i<=n;++i){
		ll c=jie[i]*njie[k]%mod*njie[i-k]%mod;
		c=c*f[i]%mod;
		if((i-k)%2) ans=(ans-c)%mod,ans=(ans+mod)%mod;
		else ans=(ans+c)%mod;
	}
	cout<<ans<<endl;
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值