Min-max 容斥与 kth 容斥

期望的线性性:

\[E(x+y)=E(x)+E(y) \]

证明:

\[E(x+y)=\sum_i \sum_j(i+j)*P(i=x,j=y) \]

\[=\sum_i\sum_ji*P(i=x,j=y)+\sum_i\sum_jj*P(i=x,j=y) \]

\[=\sum_ii*P(i=x)+\sum_jj*P(j=y) \]

\[=E(x)+E(y) \]


Min - Max 容斥:

我们现在有一个全集 \(U= \lbrace{a_1,a_2,a_3,...,a_n}\rbrace\)

我们设:

\[\begin{cases} {\max(S)=\max\limits_{a_i∈S}a_i}\\ {\min(S)=\min\limits_{a_i∈S}a_i}\\ \end{cases} \]

有:

\[\begin{cases} \max(S)=\sum_{T \in S}\limits(-1)^{|T|-1}\min(T)\\ \min(S)=\sum_{T \in S}\limits(-1)^{|T|-1}\max(T)\\ \end{cases} \]

二项式反演证明:

我们想构造一个函数 \(f\) ,使得:

\[\max(S)=\sum_{T \in S}\limits f(T)\min(T) \]

然后依然考虑一个元素排序后在哪些集合产生贡献

假设某个元素从小到大后排在第 \(x\) 位(集
合大小为 \(n\)),那么它的贡献就是:

\[[x = n] *val=\sum_{i=0}^{n-x}\left(\begin{array}{c}n-x\\ i\end{array}\right)f(i+1)*val \]

\[[x = n]=\sum_{i=0}^{n-x}\left(\begin{array}{c}n-x\\ i\end{array}\right)f(i+1) \]

变换一下:

\[[n-x = 0]=\sum_{i=0}^{n-x}\left(\begin{array}{c}n-x\\ i\end{array}\right)f(i+1) \]

\[[x = 0]=\sum_{i=0}^{x}\left(\begin{array}{c}x\\ i\end{array}\right)f(i+1) \]

二项式反演:

\[f(n+1)=\sum_{i=0}^n(-1)^{n-i}\left(\begin{array}{c}n\\ i\end{array}\right)[i=0] \]

\[f(n+1)=(-1)^{n}\left(\begin{array}{c}n\\ 0\end{array}\right)=(-1)^n \]

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

于是:

\[\max(S)=\sum_{T \in S}\limits(-1)^{|T|-1}\min(T) \]

证毕


kth 容斥:

\[Kthmax(S)=\sum_{T⊆S}(-1)^{|T|-k}\left(\begin{array}{c}{|T|-1}\\ {k-1}\end{array}\right)min(T) \]

证明:

设:

\[Kthmax(S)=\sum_{T⊆S}f(T)min(T) \]

假设某个元素从小到大后排在第 \(x\) 位(集合大小为 \(n\)),有:

\[[x =n-k+1] *val=\sum_{i=0}^{n-x}\left(\begin{array}{c}n-x\\ i\end{array}\right)f(i+1)*val \]

\[[x = n-k+1]=\sum_{i=0}^{n-x}\left(\begin{array}{c}n-x\\ i\end{array}\right)f(i+1) \]

变换一下:

\[[n-x=k-1 ]=\sum_{i=0}^{n-x}\left(\begin{array}{c}n-x\\ i\end{array}\right)f(i+1) \]

\[[x = k-1]=\sum_{i=0}^{x}\left(\begin{array}{c}x\\ i\end{array}\right)f(i+1) \]

二项式反演:

\[f(n+1)=\sum_{i=0}^{n}(-1)^{n-i}\left(\begin{array}{c}n\\ i\end{array}\right)[i=k-1] \]

\[f(n+1)=(-1)^{n-k+1}\left(\begin{array}{c}n\\ k-1\end{array}\right) \]

\[f(n)=(-1)^{n-k}\left(\begin{array}{c}n-1\\ k-1\end{array}\right) \]

于是:

\[Kthmax(S)=\sum_{T⊆S}(-1)^{|T|-k}\left(\begin{array}{c}|T|-1\\ k-1\end{array}\right)min(T) \]

证毕


Min-Max容斥定理在期望下也成立:

\[\begin{cases} E(\max(S))=\sum_{T \in S}\limits(-1)^{|T|-1}E(\min(T))\ \ (1)\\ \\ E(\min(S))=\sum_{T \in S}\limits(-1)^{|T|-1}E(\max(T))\ \ (2)\\ \\ E(Kthmax(S))=\sum_{T⊆S}(-1)^{|T|-k}\left(\begin{array}{c}|T|-1\\ k-1\end{array}\right)E(min(T))\ \ (3)\\ \end{cases} \]

\((1)\) 为例:

\[E(\max(S))=\sum_{T \in S}\limits(-1)^{|T|-1}E(\min(T)) \]

证明:

由于:

\[\max(S)=\sum_{T \in S}\limits(-1)^{|T|-1}\min(T) \]

有:

\[E(\max(S))=E(\sum_{T \in S}\limits(-1)^{|T|-1}\min(T)) \]

由期望的线性性,直接整理,得:

\[E(\max(S))=\sum_{T \in S}\limits(-1)^{|T|-1}E(\min(T)) \]

证毕

[HAOI2015]按位或

\(p(S)\) 表示选择 \(S\) 的概率,即题目输入。

\(E(\min(T))\) 表示第一次覆盖 \(T\) 中任一元素的期望操作次数。

\(P(T)\) 表示一次操作能覆盖 \(T\) 中任一元素的概率。

\[E(\min(T)) = \dfrac {1} {P(T)} \]

加法原理

\[P(T) = \displaystyle \sum _ {S \cap T \not = \varnothing} p(S) = 1 - \displaystyle \sum _ {S \cap T = \varnothing} p(S) = 1 - \displaystyle \sum _ {S \subseteq (U - T)} p(S) \]

\(P'(T) = \displaystyle \sum _ {S \subseteq T} p(S)\),则 \(P(T) = 1 - P'(U - T)\)

点击查看代码
#include<bits/stdc++.h>
using namespace std;
int n;
int cnt[1<<20];
double p[1<<20],ans;
int main(){
	scanf("%d",&n);
	for(int s=0;s<(1<<n);s++){
		scanf("%lf",&p[s]);
		cnt[s]=cnt[s>>1]+(s&1);
	}
	for(int i=1;i<(1<<n);i<<=1){
		for(int s1=0;s1<(1<<n);s1+=(i<<1)){
			for(int s2=0;s2<i;s2++){
				p[i+s1+s2]+=p[s1+s2];
			}
		}
	}
	for(int i=1;i<(1<<n);i++)if(1-p[i^((1<<n)-1)])ans+=((cnt[i]&1)?1:-1)/(1-p[i^((1<<n)-1)]);
	if(ans<1e-10)puts("INF");
	else printf("%.10lf",ans);

	return 0;
}


重返现世

题目要求第 \(k\) 小。为了方便,以下令 \(k=n-k+1\),即变为求第 \(k\) 大。

很显然,这题是让我们求这个东西:

\[\sum_{T\neq\varnothing}{|T|-1\choose k-1} (-1)^{|T|-k} min(T) \]

然而 \(n \leq 1000\) 的数据很明显不能暴力枚举每一个 \(T\)。为了优化复杂度,我们考虑一个类似于背包的 \(\text{DP}\)

\(f_{x,j,k}\) 表示前 \(x\) 个元素,满足 \(\sum p=j\),以 \(k\) 为基准的 \(\sum_T {|T|-1 \choose k-1} (-1)^{|T|-k}\) 的大小。可能你会奇怪为什么要记录 \(k\),先往后面看。

考虑转移。显然要根据 \(T\) 中是否有 \(x\) 这个元素进行分类讨论。

\(T\) 中没有 \(x\),直接转移,\(f_{x,j,k}=f_{x-1,j,k}\)

\(T\) 中有 \(x\) 时,显然前两维由 \(f_{x-1,j-v}(v=p_x)\) 转移而来,有

\[f'_{x,j,k}=\sum_{x\in T} {|T|-1 \choose k-1}(-1)^{|T|-k} \]

\(x\) 丢掉,转为考虑 \(x-1\) 时的 \(T\),此时 \(\sum_p = j-v\)

\[=\sum_T {|T| \choose k-1} (-1)^{|T|-k+1} \\ =\sum_T [{|T|-1 \choose k-1}+{|T|-1 \choose k-2}](-1)^{|T|-k+1} \\ =\sum_T {|T|-1 \choose k-1}(-1)^{|T|-k}(-1)+\sum_T {|T|-1 \choose (k-1)-1} (-1)^{|T|-(k-1)} \\ =f_{x-1,j-v,k-1}-f_{x-1,j-v,k} \]

最终我们得到转移方程:

\[f_{x,j,k}=f_{x-1,j,k}+f_{x-1,j-v,k-1}-f_{x-1,j-v,k} \]

边界条件为 \(f_{x,0,0}=1\)

发现这东西时空复杂度都是 \(O(nm(n-k))\),似乎要炸空间,所以还需要把第一维滚掉。

最后统计答案时枚举 \(\sum p\) 然后随便搞搞就好啦。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
int n,m,t;
long long dp[15][10005];
const long long md=998244353;
inline long long pwr(long long x,long long y){
	long long res=1;
	while(y){
		if(y&1)res=res*x%md;
		x=x*x%md;y>>=1;
	}return res;
}
int main(){
	scanf("%d%d%d",&n,&t,&m);
	t=n-t+1;dp[0][0]=1;
	for(int i=1;i<=n;i++){
		int p;scanf("%d",&p);
		for(int k=m;k>=p;k--){
			for(int j=t;j;j--){
				dp[j][k]=(dp[j][k]+dp[j-1][k-p]-dp[j][k-p])%md;
			}
		}
	}
	long long ans=dp[t][0];
	for(int i=1;i<=m;i++)ans=(ans+dp[t][i]*pwr(i,md-2)%md)%md;
	printf("%lld",(ans+md)*m%md);

	return 0;
}

[PKUWC2018]随机游走

由于本题要求点集 \(S\) 中所有点都被经过的步数期望,即到达点集 \(S\) 中最后一个点的期望步数。运用 \(\min-\max\) 容斥可以将其转化为到达点集 \(S\) 中第一个点的期望步数。

\(f_{S, i}\) 表示从 \(i\) 出发,经过 \(S\) 中的至少一个点的期望步数,\(deg_{i}\) 为点 \(i\) 的度数,可以得到这样的递推式:

\[\begin{aligned} f_{S, i} &=\frac{1}{\operatorname{deg}_{i}}\left(f_{S, f a_{i}}+1+\sum_{v \in s o n_{i}}\left(f_{S, v}+1\right)\right) \\ &=\frac{1}{\operatorname{deg}_{i}}\left(f_{S, f a_{i}}+\sum_{v \in s o n_{i}} f_{S, v}\right)+1 \end{aligned} \]

\(i \in S\) ,则 \(f_{i, S}=0\)

按照套路,将转移方程写成有关于父亲的函数,即 \(f_{S, i}=A_{i} \times f_{S, f a_{i}}+B_{i}\) ,分离儿子与父亲的贡献。

\[\begin{aligned} f_{S,i} &= \frac{1}{deg_i} (f_{S,fa_i} + \sum_{v \in son_i} f_{S,v}) + 1 \\ & = \frac{1}{deg_i} (f_{S,fa_i} + \sum_{v \in son_i} (A_v \times f_{S,i}+B_v)) + 1 \\ &= \frac{1}{deg_i} (f_{S,fa_i} + (\sum_{v \in son_i} A_v) \times f_{S,i} + \sum_{v \in son_i} B_v) + 1 \\ & =\frac{1}{deg_i} (f_{S,fa_i} + sumA_v \times f_{S,i} + sumB_v) + 1\\ \end{aligned} \]

\[\begin{aligned} deg_i \times f_{S,i} & = f_{S,fa_i} + sumA_v \times f_{S,i} + sumB_v +deg_i\\ (deg_i - sumA_v) \times f_{S,i}& = f_{S,fa_i} + sumB_v + deg_i \\ f_{S,i} &= \frac{1}{deg_i - sumA_v} f_{S,fa_i} + \frac{sumB_v + deg_i}{deg_i - sumA_v} \\ A_i = \frac{1}{deg_i - sumA_v} &, B_i = \frac{sumB_v + deg_i}{deg_i - sumA_v} \end{aligned} \]

发现 \(A_{i}, B_{i}\) 的方程与父亲无关,可以直接树形 \(dp\) 求解。由于 \(root\) 不能从父亲转移,显然有 \(f_{S, root}= B_{\text {root 。 }}\)

所以点集 \(S\) 的答窒为 \(\sum_{T \in S, T \neq \varnothing}(-1)^{|T|+1} f_{T, r o o t}\)

预处理所有 \(f_{S,root}\) ,再 \(\text{FWT}\) 预处理所有子集和,复杂度 \(\mathcal{O}(n2^n)\)

点击查看代码
#include<bits/stdc++.h>
using namespace std;
int n,q,rt;
int ver[45],ne[45],head[45],tot,deg[45];
inline void link(int x,int y){
	ver[++tot]=y;
	ne[tot]=head[x];
	head[x]=tot;deg[y]++;
}
long long a[21],b[21];
const long long md=998244353;
inline long long pwr(long long x,long long y){
	long long res=1;
	while(y){
		if(y&1)res=res*x%md;
		x=x*x%md;y>>=1;
	}return res;
}
void dfs(int x,int fi,int S){
	if((S>>(x-1))&1)return ;
	long long tota=0,totb=0;
	for(int i=head[x];i;i=ne[i]){
		int u=ver[i];
		if(u==fi)continue;
		dfs(u,x,S);
		tota=(tota+a[u])%md;totb=(totb+b[u])%md;
	}
	a[x]=pwr(deg[x]-tota,md-2);
	b[x]=(deg[x]+totb)%md*a[x]%md;
}
long long dp[1<<18];
int cnt[1<<18];
int main(){
	scanf("%d%d%d",&n,&q,&rt);
	for(int i=1;i<n;i++){
		int x,y;
		scanf("%d%d",&x,&y);
		link(x,y);link(y,x);
	}//puts("111");
	for(int s=1;s<(1<<n);s++){
		cnt[s]=cnt[s>>1]+(s&1);
		for(int i=1;i<=n;i++)a[i]=b[i]=0;
		dfs(rt,rt,s);dp[s]=(cnt[s]&1?1:-1)*b[rt];
	}//puts("222");
	for(int i=0;i<n;i++){
    	for(int s=0;s<(1<<n);s++){
			if((s>>i)&1)continue;
			dp[s|(1<<i)]=(dp[s|(1<<i)]+dp[s])%md;
		}
	}
	while(q--){
		int k,s=0;
		scanf("%d",&k);
		while(k--){
			int x;scanf("%d",&x);
			s|=(1<<(x-1));
		}printf("%lld\n",(dp[s]+md)%md);
	}

	return 0;
}



「JSOI2022模拟赛zjk」Festival

考虑 \(\text{min-max}\) 容后,即枚举一些位置,计算这些位置中存在一个被点亮了的期望时间。

然后考虑期望线性性,即计算 \(\sum_{i>0}P[\ i\ 步操作后这些位置都没有被点亮]\)

可以发现,计算这个概率时,最后一步操作需要满足点亮位置不是选中位置,倒数第二步操作需要满足点亮位置距离最近的选中位置距离大于 \(1\)\(\ldots\) ,考虑没有被选中的位置构成的段,设一段长度为 \(l\) ,则它在倒数第 \(1,2, \cdots\) 次操作中可以提供的方案数为 \(l, l-2, l-4, \cdots\)

因此可以求出倒数第 \(k\) 步满足条件的概率 \(pr_{k}\) ,求前缀积再求和即可。

上述过程仍然只和每一段长度有关,考虑枚举每一段长度,相当于枚举划分数。从这里开始,认为每一 段包含这一段结尾的一个选中位置,从而提供的方案数变为 \(l-1, l-3, l-5, \cdots\) 且段满足长度总和为 \(n\)

首先任意排列方案数为 \(\dfrac{\left(\sum c_{i}\right) !}{\prod c_{i} !}\) 。考虑第一段长度的期望,可以发现第一段长度为 \(i\) 的概率为 \(\dfrac{c_{i}}{\sum c_{i}}\) ,而 \(\sum i c_{i}=n\) ,因此最后的方案数即为: \(\dfrac{n *\left(\left(\sum c_{i}\right)-1\right) !}{\prod c_{i} !}\)

考虑将这个做法换成 \(dp\) 。上两部分分别给出了段数对应的期望时间和方案数。

前缀乘积的和可以看成,选择一个 \(k\) ,这之前的 \(pr_{k}\) 需要乘入贡献,之后的不需要。

考虑从小到大枚举长度,考虑设状态为,当前有 \(i\) 段,总长为 \(j\) ,当前的第一个 \(pr\) 是否乘入贡献。这样对应 \(dp_{i, j, 0 / 1 }\)

考虑计算一个 \(dp_{i, j}\) ,枚举长度为 \(1,2\) 的段数 \(x, y\) ,则可以发现此时第一个 \(pr\)\(\dfrac{j-i}{n}\) ,这一步之后,长度为 \(1,2\) 的段不会再对 \(p r\) 有影响,而刲下的段可以看成长度全部减去 \(2\),然后变成做下一步之前的情况。因此转移形如:

\[d p_{i, j, 1}=\sum_{x, y}\left(d p_{i-x-y, j-2 i+x, 0}+d p_{i-x-y, j-2 i+x, 1}\right) \times \frac{1}{x ! y !} \times \frac{j-i}{n} \\ d p_{i, j, 0}=\sum_{x, y} d p_{i-x-y, j-2 i+x, 0} \times \frac{1}{x ! y !} \]

初值为 \(d p_{0,0,0}=1\) 。最后,一个 \(d p_{i, n, 1}\) 对答案的贡献为 \(n *(i-1) ! *(-1)^{i-1}\)

但这样算出来少加了 \(1\) ,可以最后再解决。一些形式上类似的 \(dp\) 可以得到类似的效果。

在上一步的 \(dp\) 中,考虑分步枚举 \(x, y\) ,考虑先枚举 \(x\) ,从 \(d p_{i, j}\) 转移到状态 \(d p_{i-x, j-i}\) 并且这一步处理复杂度降至 \(O\left(n^{3}\right)\)

考虑记忆化搜索只处理可达的情况,且可以发现有很多转移是不需要的,实现后 \(n=500\) 只需要 \(30 \mathrm{~ms}\) ,极限数据需要 \(0.5 s\)

点击查看代码
#include<bits/stdc++.h>
using namespace std;
using ll=long long;
int md;
inline int add(int x,int y){
	return (x+=y)>=md?x-md:x;
}
inline int sub(int x,int y){
	return (x-=y)<0?x+md:x;
}
inline int ksm(long long x,int tp,int s=1){
	for(;tp;x=x*x%md,tp>>=1)if(tp&1)s=x*s%md;
	return s;
}
int n,in;
int inv[1505];
int f[1505][1505],g[1505][1505];
int s1[1505],s2[1505];
int ans;
void dp(int L){
	f[1][2*L+1]=ksm(in,L);
	for(int i=L-1;i>=0;i--){
		int x=2*i+1;
		for(int j=0;j*x<=n;j++){
			memset(g[j],0,(n+1)*sizeof(int));
		}
		g[0][n]=1ll*f[0][n]*n%md;
		g[1][2*i+1]=ksm(in,i);
		for(int j=1;j*x<=n;j++){
			memset(s1,0,j*x*sizeof(int));
			for(int k=j*x;k<=n;k++){
				g[j][k]=(g[j][k]+1ll*f[j][k]*(k-2*i*j))%md;
				if(k+x<=n)g[j+1][k+x]=(g[j+1][k+x]+1ll*f[j][k]*(j+1))%md;
				s1[k]=(s1[k-1]+1ll*f[j][k]*2*j)%md;
			}
			for(int k=j*x;k<=n;k++){
				g[j][k]=add(g[j][k],sub(s1[k-1],s1[k-x]));
			}
			if(j>1){
				memset(s1,0,j*x*sizeof(int));
				memset(s2,0,j*x*sizeof(int));
				for(int k=j*x;k<=n;k++){
					s1[k]=(s1[k-1]+1ll*f[j][k]*(j-1)%md*(x-1+k))%md;
					s2[k]=(s2[k-1]+1ll*f[j][k]*(j-1))%md;
				}
				for(int k=j*x;k<=n;k++){
					g[j-1][k]=(g[j-1][k]-1ll*(s2[k]-s2[k-x])*k+s1[k]-s1[k-x])%md;
					if(g[j-1][k]<0)g[j-1][k]+=md;
				}
			}
			else{
				for(int k=j*x;k<=n;k++){
					if(n-k<x)g[0][n]=(g[0][n]+1ll*f[j][k]*(x-1-n+k))%md;
				}
			}
		}
		for(int j=0;j*x<=n;j++){
			memcpy(f[j],g[j],(n+1)*sizeof(int));
		}
	}
}
int main(){
	freopen("festival.in","r",stdin);
	freopen("festival.out","w",stdout);
	scanf("%d %d",&n,&md);
	inv[1]=1;
	for(int i=2;i<=n;i++)inv[i]=1ll*(md-md/i)*inv[md%i]%md;
	in=inv[n];
	dp(n/2-1);
	int s=f[0][n];
	for(int i=1;i<=n;i++){
		s=(s+1ll*f[i][n]*inv[i])%md;
	}
	printf("%d\n",(n/2+1-s+md)%md);
	return 0;
}

posted @ 2021-12-14 06:49  一粒夸克  阅读(255)  评论(0编辑  收藏  举报