• bzoj2839: 集合计数


    本来是想学二项式反演的怎么我直接容斥就搞出了后面的柿子勒??

    首先当组合题做,考虑每一个大小为k的最终交集,那么就有C(n,k)种情况,对于每一种交集,可以选出2^(n-k)种包含这个集合的集合

    如果令g(k)=C(n,k)*sigema(1~2^(n-k))i C(2^(n-k),i)的话,这并不是交集大小至少为k的方案数,比如k=1时,集合是{A,B,C},{A}{B}{C}都是一个合法交集,但是选择的时候可以选出3次{A,B,C},所以我们不能直接用最基本的容斥 f(k)=sigema(k~n)i (-1)^(i-k)*g(k)来计算恰好为k的方案数

    考虑对于大小为i的集合,对于当前k,它能够产生贡献的次数C(i,k)

    于是f(k)=sigema(k~n)i (-1)^(i-k)*g(k)*C(i,k)就可以啦

    然后这个就是二项式反演的形式。。。莫名其妙搞出来了。。。

    假如从二项式反演的角度思考这个问题,首先我们能求g,然后希望得到的是交集大小为k的方案数f(k)

    考虑对于每个交集为i的方案对于g(k)的贡献

    和上面的思路几乎一样,因为每个交集为i的方案会被C(i,k)个交集为k的集合转移到

    所以g(k)=sigema(k~n)i C(i,k)*f(i)

    对于具体实现,注意nlogn会被卡,求阶乘逆元的时候可以先把inv[n]算出来再反过来推前面的

    考虑如何算g,我们知道C(0,n)+……+C(n,n)=2^n的,可以变成C(n,k)*(2^(2^(n-k))-1)

    只需要预处理2^(2^(n-k))

    稍微化一下,2^(2^(n-k))=2^(2^(n-k-1)*2)=( 2^(2^(n-k-1)) )^2,这样也可以递推了

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    
    using namespace std;
    typedef long long LL;
    const int maxn=1e6+100;
    const LL mod=1e9+7;
    LL quick_pow(LL A,LL p,LL mo)
    {
        LL ret=1;
        while(p!=0)
        {
            if(p%2==1)ret=ret*A%mo;
            A=A*A%mo;p/=2;
        }
        return ret;
    }
    
    //---------------------------def----------------------------------------------
    
    LL fac[maxn],fac_inv[maxn];
    LL C(int n,int m){return fac[n]*fac_inv[m]%mod*fac_inv[n-m]%mod;}
    
    LL g[maxn],b[maxn];
    void solve(int n)
    {
        fac[0]=1;fac_inv[0]=1;
        for(int i=1;i<=n;i++)fac[i]=fac[i-1]*i%mod;
        
        fac_inv[n]=quick_pow(fac[n],mod-2,mod);
        for(int i=n-1;i>=1;i--)fac_inv[i]=fac_inv[i+1]*(i+1)%mod;
        
        //......init......
        
        b[n]=2;
        for(int i=n-1;i>=0;i--)b[i]=b[i+1]*b[i+1]%mod;
        for(int i=0;i<=n;i++)
            g[i]=C(n,i)*(b[i]-1)%mod;
    }
    
    //---------------------------prepare----------------------------------------------
    
    int main()
    {
    //    freopen("a.in","r",stdin);
    //    freopen("a.out","w",stdout);
        int n,k;
        scanf("%d%d",&n,&k);
        solve(n);
        LL fk=0;
        for(int i=k;i<=n;i++)
            fk=(fk+(((i-k)%2==0)?1:-1)*C(i,k)*
            g[i])%mod;
        printf("%lld
    ",(fk+mod)%mod);
        
        return 0;
    }
  • 相关阅读:
    当Android端,访问WEB service 时,同样的增删改,如果我在一个函数中,实现三个业务(同一函数三个业务是相对独立的,没有交集),和用三个函数实现三个业务,哪一个比较好
    study1
    .net软件工程师面试总结(1)
    pandas 读取文件常见错误
    贝叶斯检查器
    python读取文件异常处理
    knn算法 数字文字图片识别
    饼图简单绘制
    Python自动采集数据简单操作
    数据异常值分析和处理
  • 原文地址:https://www.cnblogs.com/AKCqhzdy/p/10322789.html
Copyright © 2020-2023  润新知