学习心得 - 数学 - Min-Max 容斥【洛谷 P4707】

续集。

形如如下的式子:

\[\max(S)=\sum_{T\subseteq S,T\neq \varnothing} (-1)^{|T|-1}\min(T) \]

同理:

\[\min(S)=\sum_{T\subseteq S,T\neq \varnothing} (-1)^{|T|-1}\max(T) \]


我不会证,但是我会用举例:我们都知道下列式子成立:

\[\max(a,b)=a+b-\min(a,b) \]

同理,可以合并:

\[\max(a,b,c)=a+b+c-\min(a,b)-\min(b,c)-\min(a,c)+\min(a,b,c) \]

以此类推,我们可以推广到任意长度的 \(S\) 集合。


Min-Max 容斥可以推广到任何有关 Min-Max 的东西,比如 \(\gcd\),\(\operatorname{lcm}\)(本质是指数上的 Min-Max)还有 kth 的 Min-Max,比较复杂,以及位运算 \(\operatorname{and}\),\(\operatorname{or}\) 是位 Min-Max。

扩展 \(\min-\max\) 容斥【P4707 重返现世】

式子增加一个组合式子,如下:

\[\max_k(S)=\sum_{T\subseteq S,T\neq \varnothing} (-1)^{|T|-k}\binom{|T|-1}{k-1}\min(T) \]

例题就拿 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}\)。答案是容易的,我们考虑转移:

  1. 不选择第 \(i\) 种状态:啥都不会影响(甚至连期望值都没有!)。
  2. 选择:会影响 \(|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;
}

https://www.luogu.com.cn/article/vza4ykas

posted @ 2026-10-12 21:23  Ivan422  阅读(3)  评论(0)    收藏  举报