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;
}
posted @ 2025-04-26 19:21  ZYStream  阅读(69)  评论(0)    收藏  举报