【Luogu4707】重返现世(min-max容斥)

【Luogu4707】重返现世(min-max容斥)

题面

洛谷
求全集的(k-max)的期望

题解

(min-max)容斥的证明不难,只需要把所有元素排序之后考虑组合数的贡献,容斥系数先设出来后也不难解出。
那么我们来考虑如何求解(k-max),设出容斥系数(f(|T|))

[kmax(S)=sum_{Tsubset S}f(|T|)min(T) ]

显然是从小到大考虑每个元素作为(min)时候的贡献,并且我们只需要其中第(k)大的贡献。
假设(n=|S|),那么对于第(x)大的数,有:

[[n-x+1=k]=sum_{i=0}^{n-x}{n-xchoose i}f(i+1) ]

(x=n-x)

[[x=k-1]=sum_{i=0}^{x}{xchoose i}f(i+1) ]

直接套二项式反演:

[f(x+1)=sum_{i=0}^x(-1)^{x-i}{xchoose i}[i=k-1] ]

显然只有(i=k-1)的时候才有贡献,也就是:

[f(x+1)=(-1)^{x-k+1}{xchoose k-1} ]

化简后得到:

[f(x)=(-1)^{x-k}{x-1choose k-1} ]

好了,带回(min-max)容斥的式子,得到:

[kmax(S)=sum_{Tsubset S}(-1)^{|T|-k}{|T|-1choose k-1}min(T) ]


回到这题,要求的就是(kmax(All)),发现题目中给出了限制(|n-k|le 10),但是这样子符合条件的(T)依旧很多,那么我们按照(min(T))来分类,而(min(T))之和所有(T)中元素出现的概率的和相关,所以直接大力(dp)即可。

注意一下,题目里面的(k)对应到(kmax)的时候,应该是(n-k+1),也就是第(n-k+1)大的出现时间。
然后讲讲怎么(dp),设(f[i][j])表示当前选定的集合的(p)的和为(i),组合数那里(k=j)
对于当前物品而言,只有两种选择:要么加入集合,要么不加入。
如果不加入的话显然对于答案没有影响直接(f'[i][j] ightarrow f[i][j])
否则的话加入这个元素之后会产生影响,考虑具体的转移究竟是什么。
首先(displaystyle f[j-p][k-1]=sum(-1)^{i-(k-1)}{i-1choose k-2}Cnt[i][j-p])。其中(Cnt[i][j])表示集合大小为(i),其概率的和为(j)的方案数。
等号右边显然就是(min-max)容斥的式子,因为要求的只是方案数,所以后面并不是(min),而是方案数。
现在要做的就是强制把一个物品塞进去。
塞进去的影响是什么呢?集合大小强制多了(1),并且概率的和增加了,并且我们假装(k)也改变了。
所以塞进去后的式子是:(displaystyle sum(-1)^{i-(k-1)}{ichoose k-1}Cnt[i][j-p])
因为组合数有简单的转移式:(displaystyle {nchoose m}={n-1choose m}+{n-1choose m-1})
所以多出来的部分不难发现是(displaystyle -sum(-1)^{i-k}{i-1choose k-1}Cnt[i][j-p])
也就是(-f[j-p][k])
所以转移也就是:(f[i][j]=f'[i][j]+f'[i-p][j-1]-f'[i-p][j])

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
#define MAX 10100
#define MOD 998244353
void add(int &x,int y){x+=y;if(x>=MOD)x-=MOD;}
inline int read()
{
	int x=0;bool t=false;char ch=getchar();
	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
	if(ch=='-')t=true,ch=getchar();
	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
	return t?-x:x;
}
int n,k,m,p[1010],t,ans,N,nw,pw;
int f[2][10100][15],inv[MAX];
int main()
{
	n=read();k=n-read()+1;m=read();
	for(int i=1;i<=n;++i)p[i]=read();
	if(!k){puts("0");return 0;}
	for(int i=1;i<=k;++i)f[0][0][i]=MOD-1;
	nw=1;pw=0;
	for(int i=1,sum=0;i<=n;nw^=1,pw^=1,sum+=p[i++])
		for(int j=0;j<=sum;++j)
			for(int l=1;l<=k;++l)
				if(f[pw][j][l])
				{
					add(f[nw][j][l],f[pw][j][l]);
					add(f[nw][j+p[i]][l+1],f[pw][j][l]);
					add(f[nw][j+p[i]][l],MOD-f[pw][j][l]);
					f[pw][j][l]=0;
				}
	inv[0]=inv[1]=1;for(int i=2;i<=m;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
	for(int i=1;i<=m;++i)add(ans,1ll*f[pw][i][k]*inv[i]%MOD);
	ans=1ll*ans*m%MOD;printf("%d
",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/cjyyb/p/10223343.html