集合计数 (容斥原理)

心路:

{

想了个思路打出来硬干掉了样例,然后发现是错的....
当时直接崩了...烦躁滴很
...其实这个思路和题解大方向上是一样的,想到了用至少含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

 

posted @ 2019-07-05 14:33  three_D  阅读(833)  评论(1编辑  收藏  举报