• [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);
    }
    
    
  • 相关阅读:
    Object: Date对象——时间的设置和返回
    CSS基础知识-常用属性(updating)
    图像
    一些基础名词及含义(更新中)
    HTML常用元素合集(更新中)
    HTML小贴士-tips1
    HTML&CSS基础(一):基本格式(更新中)
    vue 轮播图显示三张图片,图片居中显示
    移动端 rem自适应布局 (750的设计稿)
    vue axios 在ios 中不执行回调(vconsole中status为0)
  • 原文地址:https://www.cnblogs.com/birchtree/p/12791661.html
Copyright © 2020-2023  润新知