zoukankan      html  css  js  c++  java
  • 集合计数(容斥原理)

    题目大意

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

    题解

      首先很容易想到从n个元素中选出k个作为交集的方案数 C(n,k)

      以下是错误思路,想偏了……(-_-)!

        然后对于每个k在剩余的n-k个元素中任选元素,与k共同组成一个集合,共有2^(n-k)种集合,这些个集合,又有2^(2^(n-k))中方案使得交集为k

      然后……就会发现有对于不同的k的方案有一堆重复(容斥啊!)……不知道咋容斥

      正解

      从剩下的n-k个元素中选元素,组成没有交集的那么多个集合,使得他们的交集只能是k

      发现上面错解里面有一些方案中的交集是k+1个元素,那么减去这些个C(n-k,1)*2^(2^(n-k-1)),这样又会多减去好几次交集为k+2的方案,再加上,

    怎样容斥就出来了

      即对于每个k而言,加上k个交集的方案,减去k+1个交集的方案,加上k+2个交集的方案,……加上(-1)^(n-k)*(n个交集的方案)

      干成一块,枚举交集i,同时不能为空集(即不能一个都不选),2^(2^(n-i))要减去全都不选的情况: ans+=(-1)^(i-k)*C(n,i)*(2^(2^(n-i))-1)*C(i,k);

      一开始我的代码濒临TLE

      

      然后学了一手线性求逆元,代码如下:   

    jc[0]=1;
    for(int i=1;i<=n;i++) jc[i]=(long long)(jc[i-1]*i)%mod;
    inv[n]=pow(jc[n],mod-2);
    for(int i=n-1;i>=0;i--) inv[i]=((inv[i+1]%mod)*(i+1)%mod)%mod;
    线性求逆元

             

      经过我的不断修改,我终于发现了30到AC的秘密

        ans+=(C(i,n)*((1ll*(pow(2,q[n-i])-1)*C(k,i)*1ll)%mod*ha*1ll))%mod; 我没写那个括号(玄学问题,我也不知道,有大佬知道的话,麻烦告诉一声,lockey在此%)

      AC代码

    #include<iostream>
    #include<cstdio>
    using namespace std;
    const long long mod=1000000007;
    int n,k;
    long long q[1100000],jc[1100000],inv[1100000];
    long long ans=0;
    long long pow(long long a,long long b){
        long long ans=1;
        a%=mod;
        while(b){
            if(b&1) ans=(long long)(ans*a)%mod;
            b>>=1;
            a=(long long)(a*a)%mod;
        }
        return ans%mod;
    }
    long long C(long long m,long long n){
        if(!m||m==n) return 1;
        if(m>n) return 0;
        if(m<mod&&n<=mod)
            return ((long long)((long long)(jc[n]*inv[m])%mod)*inv[n-m])%mod;
        return ((long long)C(m%mod,n%mod)*C(m/mod,n/mod))%mod;
    }
    int main(){
        scanf("%d%d",&n,&k);
        jc[0]=1,q[0]=1;
        for(int i=1;i<=n;i++) jc[i]=(long long)(jc[i-1]*i)%mod;
        inv[n]=pow(jc[n],mod-2);
        for(int i=n-1;i>=0;i--) inv[i]=((inv[i+1]%mod)*(i+1)%mod)%mod;
        for(int i=1;i<=n;i++) q[i]=(q[i-1]*2)%(mod-1);
        long long ha=1;
        for(int i=k;i<=n;i++,ha=-ha){
            ans+=(C(i,n)*((1ll*(pow(2,q[n-i])-1)*C(k,i)*1ll)%mod*ha*1ll))%mod;
        }
        cout<<1ll*(1ll*(ans+mod)%mod+mod)%mod<<endl;
    }
    View Code
    $Will$ $Be$ $The$ $King$
  • 相关阅读:
    C#yield return用法示例
    C#多线程示例
    AspNetCore.Authentication
    C#委托与事件
    按值和按引用传递参数
    基于iView的无限级菜单
    Sortable By Attribute
    未能加载文件或程序集“BLL”或它的某一个依赖项。生成此程序集的运行时比当前加载的运行时新,无法加载此程序集。
    有关导出Excel特殊字符的问题
    openFileDialog的使用
  • 原文地址:https://www.cnblogs.com/heoitys/p/11126819.html
Copyright © 2011-2022 走看看