zoukankan      html  css  js  c++  java
  • [BZOJ2839] 集合计数(二项式反演)

    [BZOJ2839] 集合计数(二项式反演)

    题面

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

    分析

    二项式反演套路题。把恰好转化为最少。
    (f_i)表示交集的元素个数至少为(i)的方案数。那么我们可以从(n)个元素中选出(i)个指定为交集。剩下的(n-i)个元素组成(2^{n-i})包含空集的集合。从这些集合中任意选一些集合并上那(i)个数,它们的交集一定是这(i)个数。
    因此

    [f_i=C_n^i (2^{2^{n-i}}-1) ]

    根据二项式反演,答案为

    [sum_{i=k}^n (-1)^{i-k}C_{i}^kf_i ]

    注意(2^{2^{n-i}})无法快速幂,只需要递推求即可。因为(2^{2^i}=2^{2^{i-1}} cdot2^{2^{i-1}})

    代码

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define mod 1000000007
    #define maxn 1000000
    using namespace std;
    typedef long long ll;
    inline ll fast_pow(ll x,ll k){
    	ll ans=1;
    	while(k){
    		if(k&1) ans=ans*x%mod;
    		x=x*x%mod;
    		k>>=1;
    	}
    	return ans;
    }
    inline ll inv(ll x){
    	return fast_pow(x,mod-2);
    }
    ll fact[maxn+5],invfact[maxn+5];
    void ini(int n){
    	fact[0]=1;
    	for(int i=1;i<=n;i++) fact[i]=fact[i-1]*i%mod;
    	invfact[n]=inv(fact[n]);
    	for(int i=n-1;i>=0;i--) invfact[i]=invfact[i+1]*(i+1)%mod;
    }
    ll C(int n,int m){
    	return fact[n]*invfact[n-m]%mod*invfact[m]%mod;
    } 
    
    int n,k;
    ll f[maxn+5],g[maxn+5];
    int main(){
    	scanf("%d %d",&n,&k);
    	ini(n);
    	ll pw=2;//2^(2^(n-i)),无法快速幂计算,只能递推 
    	for(int i=n;i>=0;i--){
    		f[i]=C(n,i)*(pw-1)%mod;
    		pw=pw*pw%mod;
    	}
    	ll ans=0;
    	for(int i=k;i<=n;i++){
    		if((i-k)%2==1) ans=ans-C(i,k)*f[i]%mod;
    		else ans=ans+C(i,k)*f[i]%mod;
    		ans=(ans+mod)%mod;
    	}
    	printf("%lld
    ",ans);
    }
    
    
  • 相关阅读:
    ios开发-2015-07-19
    ios开发-2015-07-18
    ios开发-2015-07-17
    ios开发-2015-07-16
    ios开发-2015-07-15
    ios开发-2015-07-14
    ios开发-2015-07-13
    Selenium源码分析之WebDriver
    webdriver实现原理 分类: Selenium 2015-07-16 00:16 11人阅读 评论(0) 收藏
    webdriver实现原理
  • 原文地址:https://www.cnblogs.com/birchtree/p/12791661.html
Copyright © 2011-2022 走看看