luoguP4707 重返现世(kthminmax容斥板题)

\(n\)个不同的数,每秒有\(p_i\)的概率取出\(i\)

问第一次出现第\(k\)个不同数的期望步数。

\(n\le 1000\)\(m\le 10000\)\(n-k\le 10\)

\(p_i\)表示为\(\frac{a_i}{m}\)的形式。


介绍个新姿势:kth-minmax容斥。

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

具体证明和普通的minmax容斥一样,同样考虑把\(S\)排序后,其中某个数\(S_i\)会算多少次。然后可以发现恰好只算到了\(S_{n-k+1}\)(注意是第\(k\)大)。

回到这题。首先\(k\leftarrow n-k+1\),即第\(k\)小变成第\(k\)大。

套式子。后面\(min\)的部分可以表示为\(\sum_{i\ge 0}(1-\sum_{j\in T}p_j)^i(\sum_{j\in T}p_j)(i+1)=\frac{1}{\sum_{j\in T}p_j}\)

列出答案式子:\(\sum_{T}(-1)^{|T|-k}\binom{|T|-1}{k-1}\frac{1}{\sum_{j\in T}p_j}\),考虑把\(\sum_{j\in T}p_j\)相同的一起算,得到朴素的DP做法,记下当前搞到哪个数,\(|T|\)为多少,\(\sum_{j\in T}p_j\)为多少。

实际上可以发现:\((-1)^{|T|-k}\binom{|T|-1}{k-1}=(x-1)^{|T|-1}[x^{k-1}]\)

于是改进DP:设\(f_{i,j}\),考虑前\(i\)个数,\(\sum_{j\in T}p_j\)\(j\),存下一个多项式。转移的时候乘\((x-1)\)

因为\(k\le 11\),时间是\(O(nm(n-k))\),能过。

细节:初值\(f_{0,0}=\frac{1}{x-1}\),不要忘了。


using namespace std;
#include <bits/stdc++.h>
#define N 1005
#define M 10005
#define K 13
#define ll long long
#define mo 998244353
ll qpow(ll x,ll y=mo-2){
	ll r=1;
	for (;y;y>>=1,x=x*x%mo)
		if (y&1)
			r=r*x%mo;
	return r;
}
int n,k,m;
int p[N];
int C[N][N];
int f[M][K];
int main(){
//	freopen("in.txt","r",stdin);
	scanf("%d%d%d",&n,&k,&m);
	k=n-k+1;
	for (int i=1;i<=n;++i)
		scanf("%d",&p[i]);
	for (int i=0;i<=n;++i){
		C[i][0]=1;
		for (int j=1;j<=i;++j)
			C[i][j]=(C[i-1][j-1]+C[i-1][j])%mo;
	}
	for (int i=0;i<k;++i)	
		f[0][i]=-1;
	for (int i=0;i<n;++i)
		for (int j=m-p[i+1];j>=0;--j){
			(f[j+p[i+1]][0]-=f[j][0])%=mo;
			for (int t=1;t<k;++t)
				f[j+p[i+1]][t]=((ll)f[j+p[i+1]][t]-f[j][t]+f[j][t-1])%mo;
		}
	ll ans=0;
	for (int j=1;j<=m;++j)
		(ans+=qpow(j)*f[j][k-1])%=mo;
	ans=(ans+mo)%mo*m%mo;
	printf("%lld\n",ans);
	return 0;
}
posted @ 2021-03-25 21:10  jz_597  阅读(60)  评论(0编辑  收藏  举报