P10596 BZOJ2839 集合计数 题解
前置知识
解法
考虑二项式反演。
设 \(f_{m}\) 表示交集中恰好有 \(m\) 个元素的方案数,\(g_{m}\) 表示交集中至少有 \(m\) 个元素的方案数,即 \(g_{m}=\sum\limits_{i=m}^{n}\dbinom{i}{m}f_{i}\),那么有 \(f_{m}=\sum\limits_{i=m}^{n}(-1)^{i-m}\dbinom{i}{m}g_{i}\)。
现在问题转化为怎么求 \(g_{m}\)。先钦定选出的 \(m\) 个元素,此时方案数为 \(\dbinom{n}{m}\);接着剩下的 \(n-m\) 个元素一共可以组成 \(2^{n-m}\) 个集合(包括空集),又因为这些集合中至少要选 \(1\) 个,故状态转移方程为 \(g_{m}=\dbinom{n}{m}(2^{2^{n-m}}-1)\)。
由 \(\begin{aligned} 2^{2^{n-m+1}}=2^{2^{n-m} \times 2}=2^{2^{n-m}+2^{n-m}}=(2^{2^{n-m}})^{2} \end{aligned}\) 倒序处理即可。
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define sort stable_sort
#define endl '\n'
const ll p=1000000007;
ll g[1000010],jc[1000010],inv[1000010],jc_inv[1000010];
ll C(ll n,ll m,ll p)
{
return (n>=m&&n>=0&&m>=0)?((jc[n]*jc_inv[m]%p)*jc_inv[n-m]%p):0;
}
int main()
{
ll n,k,i,mi=2,f=0;
cin>>n>>k;
inv[1]=1;
jc[0]=jc_inv[0]=jc[1]=jc_inv[1]=1;
for(i=2;i<=n;i++)
{
inv[i]=(p-p/i)*inv[p%i]%p;
jc[i]=jc[i-1]*i%p;
jc_inv[i]=jc_inv[i-1]*inv[i]%p;
}
for(i=n;i>=k;i--)
{
g[i]=C(n,i,p)*mi%p;
mi=mi*mi%p;
}
for(i=k;i<=n;i++)
{
f=(f+((i-k)%2==0?1:-1)*C(i,k,p)*g[i]%p+p)%p;
}
cout<<f<<endl;
return 0;
}
本文来自博客园,作者:hzoi_Shadow,原文链接:https://www.cnblogs.com/The-Shadow-Dragon/p/18265965,未经允许严禁转载。
版权声明:本作品采用 「署名-非商业性使用-相同方式共享 4.0 国际」许可协议(CC BY-NC-SA 4.0) 进行许可。