BZOJ2839 集合计数 题解
引入
关于子集个数
关于 \(n\) 个元素的集合 \(U\) 的子集个数,有两种理解方式。
- 组合数理解
从\(n\) 个元素的集合中能够选择构造出 \(\binom{n}{0}\) 个空集, $\binom{n}{1} $ 个元素个数为 $ 1$ 的集合,以此类推,子集个数即为\[\left | \left \{ S | S \in U \right \} \right | =\sum_{i=0}^{n} \binom{n}{i} = 2^n \] - 01状态理解
有 \(n\) 个位置,每个位置上的元素对应集合 \(U\) 的对应元素,每个位置有选或不选两个决策,即\[\left | \left \{ S | S \in U \right \} \right | =2^n \] 
关于二项式反演的其中一个形式
对于集合 \(U\) 和 \(n\) 个属性 \(S_i\),令 \(f(x)\) 表示至少「钦定」\(x\) 个属性的方案数,「钦定」的意义即
\[f(x)=\sum_{ \left \{ T_i \right \}_{i=1}^{x} \subseteq \left \{ S_j \right \}_{j=1}^{n}  } \left | \bigcap_{i=1}^{x} T_i \right |
\]
令 \(g(x)\) 为恰好涉及 \(x\) 个属性的方案数,则有如下
\[f(x)=\sum_{i=x}^{n} \binom{i}{x} g(i) \Longleftrightarrow g(x)=\sum_{i=x}^{n}(-1)^{i-x} \binom{i}{x}f(i)
\]
题解
设集合 \(S\) 表示集合 \(U\) 的 \(2^n\) 个子集构成的集合,\(\left | S \right |=2^n\),题目让求的是有多少个方案满足 \(S\) 的子集 \(T\) 使得 \(\left | \bigcap_{T \subseteq S} T \right |=k\) ,考虑二项式反演,令 \(f(x)\) 表示「钦定」交集的大小为 \(x\) 的集合的方案数,则有
\[f(x)=\binom{n}{x}(2^{2^{n-x}}-1)
\]
解释一下,满足大小为 \(x\) 的情况有 \(\binom{n}{x}\) 个元素,剩余可以选 \(2^{n-x}\) 个集合,其子集有 \(2^{2^{n-x}}\) 个集合,但有一个是空集,减去一个 \(1\) 即可。令 \(g(x)\) 表示交际大小恰好为 \(x\) 的方案数,则有
\[g(x)=\sum_{i=x}^{n} (-1)^{i-x}\binom{i}{x}f(i)
\]
将 \(f(x)\) 代入原式,\(g(k)\) 即为最终答案
\[Ans=\sum_{i=k}^{n} (-1)^{i-k}\binom{n}{i} \binom{i}{k}(2^{2^{n-i}}-1)
\]
在计算 \(2^{2^{n-i}} \mod P\) 时,需要用到扩展欧拉定理,即 \(2^{2^{n-i}} \equiv 2^{2^{n-i} \mod (P-1)} \pmod{P}\)
CODE
#include<cstdio>
using namespace std;
const int N=1e6+5;
const int P=1e9+7;
typedef long long ll;
ll fc[N];
ll infc[N];
ll n,k,ans;
ll binpow(ll a,ll b,ll mod){
    ll res=1;
    while (b){
        if (b&1) res=(res*a)%mod;
        a=(a*a)%mod;
        b>>=1;
    }
    return res%mod;
}
void fact_init(){
    fc[0]=1;
    for (int i=1;i<=n;i++) fc[i]=(fc[i-1]*i)%P;
    for (int i=0;i<=n;i++) infc[i]=binpow(fc[i],P-2,P)%P;
}
ll C(ll n,ll m){
    return fc[n]%P*infc[m]%P*infc[n-m]%P;
}
int main(){
    scanf("%lld%lld",&n,&k);
    fact_init();
    for (int i=k;i<=n;i++){
        ll t=(((C(n,i)*C(i,k))%P)*(binpow(2,binpow(2,n-i,P-1),P)-1)%P)%P;
        if ((i-k)%2==1) t*=-1;
        ans=((ans+t)%P+P)%P;
    }
    printf("%lld\n",ans);
    return 0;
}
                    
                
                
            
        
浙公网安备 33010602011771号