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

    心路:

    {

    想了个思路打出来硬干掉了样例,然后发现是错的....
    当时直接崩了...烦躁滴很
    ...其实这个思路和题解大方向上是一样的,想到了用至少含k个的方案数减去含k+1个的加上k+2的。。。

    然后再想怎么求至少含k个的方案数想到了让集合含这k个数然后随机组就行,但没有想出来怎么求含这k个数的集合数,而且就算求出来发现不能直接对一种情况乘,有重复计算的...

    想到这...再想...再想...放弃->颓题解。T_T
    颓完题解,发现我是把题目信息忽略了...2^n没怎么用上;

    }

    题解

    {

      往容斥上想,容斥的一般思路就是先找出单一满足的,把问题简单化(让其限制变小),再逐步用容斥求解。

    这题总思路就是先找至少含k个的(限制变少,能够推式),再容斥。

    再求至少含k个的时候,容易发现想要交出含这k个的,让集合含这k个数在随机组合一定能交出来.

    问题来了:怎么求含特定k个数的集合总数呢?

    把这k个数提取出来剩下的数随机组,能够组成的集合在把k个数补回去不就是答案吗,所以有2^(n-k)种(含空集,实际指的是正好只含这K个数的集合),在让这几个集合随机组一定是满足能交成至少含k个数的方案。注意空集不是,所以是2^( 2^(n-k) )-1种。

    举个栗子:数据是4 2

    以1,3为例,把1 3提取,剩下的数所组是{2},{4},{2,4},{空},其实就是{1,2,3},{1,3,4},{1,2,3,4},{1,3};这四个集合在随机组成的方案中,空集相当于哪个集合都没取交集为空所以不符合。

    求出1,3后乘上C(n,2)不就是交出来至少含k个的方案数了吗?显然不是,,,有重复的啊

    比如1,3会求到{1,2,3,4}交{1,3,4},而1,4..3,4也会(当时我就这崩了...)

    看重复的有多少啊->对于求k个时交出来是k+1个的会算C(k+1,k)遍以此类推..所以在容斥时只要把重复的倍数减去就行。

    设f(k)=C(n,k)*(  2^( 2^(n-k) )-1 ),

    答案就是f(k)*C(K,K)-C(K+1,K)*f(k+1)+C(k+2,k)*f(k+2)...;

    预处理阶乘和逆元,2^...这里也要预处理不然会WA(我也不知道为哈用快速幂求出的大数据就是不对)。

    2^(2^(n-k))=( 2^(2^(n-k-1)) )^2,利用这个性质倒推预处理出来就行了

    f(k)Ckkf(k+1)Ckk+1+f(k+2)Ckk+2...f(n)Ckn

    }

    #include<cstdio>
    #include<iostream>
    using namespace std;
    #define ll long long
    const int mod=1e9+7;
    const int maxn=1000010;
    int n,k;
    ll ans,f[maxn];
    ll fac[maxn],inv[maxn],qtwo[maxn];
    ll qpow(ll a,int b)
    {
        ll ans=1;
        while(b)
        {
            if(b&1) ans=ans*a%mod;
            b>>=1;
            a=a*a%mod;
        }
        return ans;
    }
    void init()
    {
        fac[0]=1;
        for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%mod;
        inv[n]=qpow(fac[n],mod-2);
        for(int i=n-1;i>=0;i--) inv[i]=inv[i+1]*(i+1)%mod;
        qtwo[n]=2;
        for(int i=n-1;i>=0;i--) qtwo[i]=qtwo[i+1]*qtwo[i+1]%mod;
    }
    void F(int x)
    {
        ll turn,kp;
        turn=qtwo[x]-1;
        f[x]=fac[n]*inv[x]%mod*inv[n-x]%mod*turn%mod;
    }
    void rongchi()
    {
        for(int i=k;i<=n;i+=2) ans=(ans+ fac[i]*inv[k]%mod*inv[i-k]%mod*f[i]%mod )%mod;
        for(int i=k+1;i<=n;i+=2) ans=(ans+mod- fac[i]*inv[k]%mod*inv[i-k]%mod*f[i]%mod )%mod;
    }
    int main()
    {
    //freopen("c.out","w",stdout);
        scanf("%d%d",&n,&k);
        init();
        for(int i=k;i<=n;i++) F(i);
        rongchi();
        printf("%lld",ans);
    }
    View Code
  • 相关阅读:
    linux系统日志使用
    C# progressbar 用法
    python 新时代
    linux 常用命令 集锦
    c# DirectoryInfo类 详解
    哈佛(转)
    寒门难再出贵子
    排序算法
    JavaScript弹出框
    js中innerHTML与innerText的用法与区别
  • 原文地址:https://www.cnblogs.com/three-D/p/11138164.html
Copyright © 2011-2022 走看看