学习心得 - 数学 - Min-Max 容斥【洛谷 P4707】
续集。
形如如下的式子:
同理:
我不会证,但是我会用举例:我们都知道下列式子成立:
同理,可以合并:
以此类推,我们可以推广到任意长度的 \(S\) 集合。
Min-Max 容斥可以推广到任何有关 Min-Max 的东西,比如 \(\gcd\),\(\operatorname{lcm}\)(本质是指数上的 Min-Max)还有 kth 的 Min-Max,比较复杂,以及位运算 \(\operatorname{and}\),\(\operatorname{or}\) 是位 Min-Max。
扩展 \(\min-\max\) 容斥【P4707 重返现世】
式子增加一个组合式子,如下:
例题就拿 P4707 吧,题目大意:有 \(n\) 种材料,你需要其中任意 \(k\) 种,每一个都有一个概率出现。问收集到 \(k\) 种的期望时间。
首先我们 Min-Max 容斥都存在的共性是可期望性。怎么建模成集合?考虑最终的集合 \(S\),显然期望时间就是 \(E(\min_k(S))\),也就是只看种类,第 \(k\) 小的时间。显然我们不好看这个,那么就转化为 \(E(\max_k(S))\),即 \(n-k+1\to k\)。
然后 \(n\le 1000\),显然不能所有集合 \(T\) 都求一遍答案。但是 \(\color{red}n-k+1\le 10\)(题目特殊条件),考虑如何利用这个。目前我们转化后,显然就有 \(k\le 10\)。那么我们就可以使用 \(\mathcal O(nmk)\) 的 DP?卡卡空间试试。
首先我们设 \(f_{i,j,k}\) 为前 \(i\) 种材料,选择了其中 \(j\) 种,然后目前期望值为 \(k\) 的 \(\min(T)\) 系数(显然 \(\min(T)=k\)),也就是 \(=\sum_{T\subseteq S,T\neq \varnothing} (-1)^{|T|-k}\binom{|T|-1}{k-1}\)。答案是容易的,我们考虑转移:
- 不选择第 \(i\) 种状态:啥都不会影响(甚至连期望值都没有!)。
- 选择:会影响 \(|T|-k\) 的值和 \(\binom{|T|-1}{k-1}\)。首先 \(|T|-k\) 直接正负性更改,然后后面这一项就考虑本质:\(\binom{|T|}{k-1}=\binom{|T|-1}{k-1}+\binom{|T|-1}{k-2}\),考虑这两个所属的子状态,拆分一下可以得到:\(f_{i,j,k}=f_{i-1,j-1,k-p_i}-f_{i-1,j,k-p_i}\)。
然后最后一个问题:\(f_{0,j,0}\) 的初始化。如何保证 \(|T|=k\) 贡献为 \(1\)?就可以用 \(f_{0,0,0}=0\),此时不会有影响,其余都赋值成 \(-1\)。考虑 \(-f_{i-1,j,k-p_i}\),此时我们不能让 \(f_{0,0,0}\) 有 \(f_{i-1,j-1,k-p_i}\) 的贡献,但是如果其他都是 \(-1\),此时就可以存在 \(1\) 的贡献,就可以计算出来了。
空间压掉无关紧要的 \(i\) 即可。代码回来写。
// #define File
#include<bits/stdc++.h>
#define ll long long
#define ld long double
#define fi first
#define se second
using namespace std;
const ll inf=1e12, P=998244353;
const int N=1e3+10, M=1e4+10;
ll qp(ll a,ll b){
ll res=1;
for(;b;b>>=1,a=(a*a)%P) if(b&1) res=(res*a)%P;
return res;
}
ll f[15][M];
int n,m,kl,p[N];
void solve(){
cin>>n>>kl>>m;
kl=n-kl+1;
for(int i=1;i<=n;i++) cin>>p[i];
for(int j=1;j<=kl;j++) f[j][0]=-1;
for(int i=1;i<=n;i++) for(int j=kl;j>=1;j--)
for(int k=m;k>=p[i];k--) f[j][k]=(f[j][k]+((f[j-1][k-p[i]]-f[j][k-p[i]])%P+P)%P)%P;
ll ans=0;
for(int i=1;i<=m;i++)
ans=(ans+f[kl][i]*qp(i,P-2)%P)%P;
cout<<ans*m%P;
return;
}
int main(){
#ifdef File
freopen("readfile.in","r",stdin);
freopen("readfile.out","w",stdout);
#endif
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int T=1; // cin>>T;
while(T--) solve();
return 0;
}
文章同步载入于洛谷,详见文章下方链接。
本文来自博客园,作者:Ivan422,转载请注明原文链接:https://www.cnblogs.com/Exfish/p/23277087。

浙公网安备 33010602011771号