首页 > 其他分享 >【瞎口胡】Min-Max 容斥

【瞎口胡】Min-Max 容斥

时间:2022-08-31 20:25:15浏览次数:95  
标签:dbinom limits int Max sum 瞎口 容斥 leq dp

Min-Max 容斥是通过容斥集合的最小值来得到集合最大值的一种方法。结合期望的线性性,我们得以计算几个随机变量最值的期望,它往往不和这些变量期望的最值相等。

Min-Max 容斥

Min-Max 容斥表明,对于集合 \(S\),

\[\max(S) = \sum \limits_{T \subseteq S} (-1)^{|T|+1} \min(T) \]

因为 \(S\) 是一个集合,因此其中的元素互不相同(如果实际应用时有重复元素,就按照编号排序)。设 \(A_k\) 表示 \(S\) 中第 \(k\) 大的元素,然后依次考虑对于每个 \(A_k\),它对等式右边的贡献。

  • \(k=1\)

    当且仅当 \(T = \{A_1\}\) 时 \(\min(T) = A_1\),因此 \(A_1\) 的贡献为 \((-1)^2A_1 = A_1\)。

  • \(k>1\)

    此时集合只能由 \(A_1 \sim A_{k-1}\) 中的任意多个加上 \(A_k\) 构成,这样的集合一共有 \(2^{k-1}\) 个,显然有 \(2^{k-2}\) 个集合大小为奇数,\(2^{k-1}\) 个集合大小为奇数,\(A_k\) 的贡献为 \((2^{k-2}-2^{k-2})A_k = 0\)。

因此最后等式右边只剩下 \(A_1\),它就是 \(\max(S)\)。

同样地,我们有

\[\min(S) = \sum \limits_{T \subseteq S} (-1)^{|T|+1} \max(T) \]

注意到 \(\min\) 和 \(\max\) 本来就对称,对上面的证明稍作修改即可。

扩展 Min-Max 容斥

Min-Max 容斥可以扩展到 \(k\) 大。我们还是希望对 \(\min(T)\) 容斥得到 \(\operatorname{kthmax}(S)\),但容斥系数 \(f(|T|)\) 可能和上面不同,即

\[\operatorname{kthmax}(S) = \sum \limits_{T \subseteq S} f(|T|) \min(T) \]

考虑 \(A_p\) 对等式右边的贡献系数。根据上面的讨论,如果 \(\min(T)=A_p\),集合只能由 \(A_1\sim A_p\) 中的若干个组成,且必须包含 \(A_p\)。我们希望仅当 \(p=k\) 时,\(A_p\) 贡献 \(1\) 的系数,其它时候都是 \(0\),于是有

\[\sum \limits_{i=0}^{p-1} \dbinom{p-1}{i} f(i+1) = [p=k] \]

用 \(p+1\) 替换 \(p\),有

\[\sum \limits_{i=0}^{p} \dbinom{p}{i} f(i+1) = [p=k-1] \]

令 \(G(i) = f(i+1),F(i) = [i=k-1]\),则

\[F(n) = \sum \limits_{i=0}^n \dbinom{n}{i} G(i) \]

二项式反演得

\[G(n) = \sum \limits_{i=0}^n (-1)^{n-i} \dbinom{n}{i} F(i) \]

展开得

\[f(n+1) = \sum \limits_{i=0}^n (-1)^{n-i} \dbinom{n}{i} [i=k-1] = (-1)^{n-k+1} \dbinom{n}{k-1} \]

\[f(n) = (-1)^{n-k} \dbinom{n-1}{k-1} \]

因此

\[\operatorname{kthmax}(S) = \sum \limits_{T \subseteq S} (-1)^{|T|-k} \dbinom{|T|-1}{k-1} \min(T) \]

Min-Max 容斥和期望

根据期望的线性性,有 \(E[X+Y] = E[X]+E[Y]\)。因为在 Min-Max 容斥的过程中,我们只使用了线性运算,于是它对期望成立,这几乎是显然的。也就是说,

\[E(\max(S)) = \sum \limits_{T \subseteq S} (-1)^{|T|+1} E(\min(T)) \]

\[E(\operatorname{kthmax}(S))= \sum \limits_{T \subseteq S} (-1)^{|T|-k} \dbinom{|T|-1}{k-1} E(\min(T)) \]

例 1 [HAOI2015]按位或

题意

有一个数字 \(v\),初值为 \(0\)。每秒会随机选择一个 \([0,2^n-1]\) 的数字 \(x\),令 \(v\) 变为 \(v \operatorname {or} x\),其中 \(\operatorname{or}\) 是按位或。选中 \(x\) 的概率是 \(p_x\)。

问 \(v\) 期望多少秒变成 \(2^n-1\),或判断无解。

\(0 \leq n \leq 20,0 \leq p_x \leq 1,\sum p_x = 1\)

题解

注意到位之间独立,可以看作一个 \(n\) 元素集合,每秒的操作看作选出一个它的子集,于是我们设全集 \(U\) 为全部 \(n\) 个元素都被选择过的时间,要计算的就是 \(E(\max(U))\)。

根据 Min-Max 容斥,

\[E(\max(S)) = \sum \limits_{T \subseteq S} (-1)^{|T|+1} E(\min(T)) \]

只需要计算 \(E(\min(T))\) 即可,即集合 \(T\) 中有至少一个元素被选择过的期望时间。如果我们知道了每次选出的集合 \(G\) 和 \(T\) 有交的概率 \(p_0\),那么期望时间就是 \(\dfrac{1}{p_0}\)。

为此,我们希望计算所有满足 \(G \cap T \neq \varnothing\) 的集合 \(G\) 的 \(p_G\) 之和。注意到这有点难算,不妨取 \(T\) 的补集 \(T^c\),其子集一定和 \(T\) 没有交。现在的问题就是计算 \(f_{S_0} = \sum \limits_{T_0 \cup S_0 = S_0} p_{S_0}\)。FWT 即可。

最后的答案即

\[\sum \limits_{T \subseteq U} (-1)^{|T|+1} \dfrac{1}{1-p_{U\backslash T}} \]

为什么是 \(1-p_{U\backslash T}\) 呢,因为 \(p_{U\backslash T}\) 是选不到和 \(T\) 有交的集合的概率,注意 \(E(\min(\varnothing))\)就别用 \(\dfrac{1}{1-1}\) 来算了,直接跳过这一项就好,反正它等于 \(0\)。

时间复杂度 \(O(2^n)\)。

# include <bits/stdc++.h>

const int N=1048580;

int n;
double p[N];
int bc[N];

int main(void){
	scanf("%d",&n);
	for(int i=0;i<(1<<n);++i)
		scanf("%lf",&p[i]);
	for(int i=1;i<(1<<n);++i)
		bc[i]=bc[i>>1]+(i&1); 
	for(int l=2,s=1;l<=(1<<n);l<<=1,s<<=1){
		for(int i=0;i<(1<<n);i+=l){
			for(int j=0;j<s;++j)
				p[i+j+s]+=p[i+j];
		}
	}
	int U=(1<<n)-1;
	double ans=0;
	for(int i=1;i<=U;++i){
		if(1-p[U^i]<1e-10){
			printf("INF");
			return 0;
		}
		ans+=1.0/(1-p[U^i])*((bc[i]&1)?1:-1);
	}
	printf("%.8lf",ans);
	return 0;
}

例 2 Luogu P4707 重返现世

题意

有 \(n\) 种原料,每一秒会随机出现一种原料,第 \(i\) 种原料出现的概率是 \(\dfrac{p_i}{m}\)。如果之前没有这种原料,主人公就会对它进行收集。求主人公收集到 \(k\) 种原料的期望时间。

\(1 \leq n \leq 1000,1 \leq k \leq n,0 \leq n-k \leq \textbf{10},0 \leq p_i \leq m,\sum p_i =m,1 \leq m \leq 10^4\)

题解

一眼暴力扩展 Min-Max 容斥。

\[E(\operatorname{kthmin}(S))= \sum \limits_{T \subseteq S} (-1)^{|T|-k} \dbinom{|T|-1}{k-1} E(\max(T)) \]

我们发现 \(\max(T)\) 不怎么好算。不妨将 \(k\) 变为 \(n-k+1\),这样 \(k\) 小变成了 \(k\) 大,并且还有一个好性质:现在 \(1 \leq k \leq 11\) 了!

\[E(\operatorname{kthmax}(S))= \sum \limits_{T \subseteq S} (-1)^{|T|-k} \dbinom{|T|-1}{k-1} E(\min(T)) \]

后面的 \(E(\min(T))\) 等于 \(\dfrac{m}{\sum \limits_{i \in T} p_i}\)。如果暴力算会寄掉,但因为 \(m \leq 10^4\),我们可以考虑一些依赖 \(m\) 的算法。考虑到,如果 \(|T|\) 相同,\(\sum \limits_{i \in T} p_i\) 相同,那么它们对答案的贡献就应该相同。

尝试把这些状态分到一类,设 \(dp(i,j,s)\) 表示前 \(i\) 个数选了 \(j\) 个,\(p_i\) 之和等于 \(s\) 的方案数。考虑刷表, \(dp(i-1,j,s)\) 可以刷到 \(dp(i,j,s)\) 和 \(dp(i,j+1,s+p_i)\)。

复杂度 \(O(n^2m)\),滚动数组可以 \(70\) 分。要优化只能从 \(j\) 这一维下手,砍掉这一维变成了 \(dp(i,s)\),表示前 \(i\) 个数 \(p_i\) 之和等于 \(s\) 的方案中 \((-1)^{|T|-k} \dbinom{|T|-1}{k-1}\) 的和。

然后考虑下该怎么转移。不选还是一样的,从 \(dp(i-1,s)\),刷到 \(dp(i,s)\);如果要选 \(p_i\) 的话非常头疼,\(dp(i-1,s)\) 中的贡献到了 \(dp(i,s+p_i)\) 就变成了

\[(-1)^{|T|+1-k} \dbinom{|T|}{k-1} \]

考虑怎样把这部分贡献用若干个 \(dp\) 值表示出来。拆掉组合数,变成

\[(-1)^{|T|+1-k} \dbinom{|T|-1}{k-2} + (-1)^{|T|+1-k} \dbinom{|T|-1}{k-1} \]

\[=(-1)^{|T|-(k-1)} \dbinom{|T|-1}{k-2} - (-1)^{|T|-k} \dbinom{|T|-1}{k-1} \]

右边那一坨就是 \(dp(i-1,s)\),左边那一坨和右边很像啊...只有 \(k\) 有点差别,把 \(k\) 记进去就好了。

于是正确的方式:设 \(dp(i,k,s)\),刷的时候 \(dp(i-1,k,s)\) 让 \(dp(i,k,s)\) 加上 \(dp(i-1,k,s)\),让 \(dp(i,k,s+p_i)\) 加上 \(dp(i-1,k-1,s)-dp(i-1,k,s)\) 即可。

关于边界:\(dp(0,0,0)=0\),\(dp(0,i,0)=1(i>0)\)。考虑 \(k=1\) 时需要将上式拓展到广义组合数意义下,容易知道 \(\dbinom{n}{-1}=0,\dbinom{n}{0}=1 (n \in \R)\)。

代码里面是填表法,跟背包差不多,好处是少了点边界情况。

# include <bits/stdc++.h>

const int N=10010,INF=0x3f3f3f3f,MOD=998244353;

int n,k,m;
int dp[N][12];
int p[N];

inline int read(void){
	int res,f=1;
	char c;
	while((c=getchar())<'0'||c>'9')
		if(c=='-')f=-1;
	res=c-48;
	while((c=getchar())>='0'&&c<='9')
		res=res*10+c-48;
	return res*f;
}
inline int qpow(int d,int p){
	int ans=1;
	for(;p;p>>=1,d=1ll*d*d%MOD) (p&1)&&(ans=1ll*ans*d%MOD);
	return ans;
} 

int main(void){
	n=read(),k=read(),m=read(),k=n-k+1;
	for(int i=1;i<=n;++i) p[i]=read();
	for(int i=1;i<=k;++i) dp[0][i]=-1;
	for(int i=1;i<=n;++i){
		for(int j=m;j>=p[i]&&p[i];--j)
			for(int kp=k;kp;--kp) dp[j][kp]=((dp[j][kp]+dp[j-p[i]][kp-1]-dp[j-p[i]][kp])%MOD+MOD)%MOD;
	}
	int ans=0;
	for(int i=1;i<=m;++i) ans=(ans+1ll*m*qpow(i,MOD-2)%MOD*dp[i][k]%MOD)%MOD;
	printf("%d",ans);
	
	return 0;
}

标签:dbinom,limits,int,Max,sum,瞎口,容斥,leq,dp
From: https://www.cnblogs.com/liuzongxin/p/16644403.html

相关文章