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

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

题面

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

题解

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

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

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

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

\(x=n-x\)

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

直接套二项式反演:

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

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

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

化简后得到:

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

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

\[kmax(S)=\sum_{T\subset S}(-1)^{|T|-k}{|T|-1\choose 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]\rightarrow f[i][j]\)
否则的话加入这个元素之后会产生影响,考虑具体的转移究竟是什么。
首先\(\displaystyle f[j-p][k-1]=\sum(-1)^{i-(k-1)}{i-1\choose k-2}Cnt[i][j-p]\)。其中\(Cnt[i][j]\)表示集合大小为\(i\),其概率的和为\(j\)的方案数。
等号右边显然就是\(min-max\)容斥的式子,因为要求的只是方案数,所以后面并不是\(min\),而是方案数。
现在要做的就是强制把一个物品塞进去。
塞进去的影响是什么呢?集合大小强制多了\(1\),并且概率的和增加了,并且我们假装\(k\)也改变了。
所以塞进去后的式子是:\(\displaystyle \sum(-1)^{i-(k-1)}{i\choose k-1}Cnt[i][j-p]\)
因为组合数有简单的转移式:\(\displaystyle {n\choose m}={n-1\choose m}+{n-1\choose m-1}\)
所以多出来的部分不难发现是\(\displaystyle -\sum(-1)^{i-k}{i-1\choose 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\n",ans);
	return 0;
}
posted @ 2019-01-05 09:07  小蒟蒻yyb  阅读(411)  评论(1编辑  收藏  举报