UOJ #390 - 【UNR #3】百鸽笼

发布时间 2023-07-14 16:37:20作者: tzc_wk

考虑转化模型(有点类似于 PKUSC2018 猎人杀):生成一个值域为 \([1,n]\) 的无穷序列,记 \(b_i\) 表示其中第 \(a_i\)\(i\) 的位置,那么所求即为 \(b_i\)\(b\) 序列中的最大值的概率。

容斥。假设我们要计算 \(x\) 的答案,我们考虑钦定一个集合 \(S\) 满足 \(S\) 中的所有元素的 \(b\) 都大于 \(b_x\)(即在第 \(a_x\)\(x\) 前的出现次数 \(<b_x\)),那么我们考虑 \(dp_{i,j,k}\) 表示考虑到前 \(i\) 个数,钦定了 \(j\) 个数,它们总出现次数为 \(k\) 的方案数,转移是 \(O(n)\) 的,最后计算答案的时候发现贡献系数只取决于 \(j\)\(k\),因为其实不在 \(S\) 中的数我们可以忽略不计,相当于每个数生成的概率为 \(\dfrac{1}{j+1}\),所以一对 \(j,k\) 的贡献系数就是 \((-1)^j·(\dfrac{1}{j+1})^{k+a_x}\)

时间复杂度 \(O(n^6)\),可以优化到 \(O(n^5)\),但是直接实现就可以通过。

const int MAXN=30;
const int MAXS=900;
const int MOD=998244353;
int qpow(int x,int e){int ret=1;for(;e;e>>=1,x=1ll*x*x%MOD)if(e&1)ret=1ll*ret*x%MOD;return ret;}
int n,a[MAXN+5],c[MAXS+5][MAXS+5],pw[MAXN+5][MAXS+5];
int main(){
	scanf("%d",&n);for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	for(int i=0;i<=MAXS;i++){
		c[i][0]=1;
		for(int j=1;j<=i;j++)c[i][j]=(c[i-1][j]+c[i-1][j-1])%MOD;
	}
	for(int i=1;i<=n;i++){
		int iv=qpow(i,MOD-2);
		for(int j=(pw[i][0]=1);j<=MAXS;j++)pw[i][j]=1ll*pw[i][j-1]*iv%MOD;
	}
	for(int t=1;t<=n;t++){
		static int dp[MAXN+5][MAXS+5];int sum=0,res=0;
		memset(dp,0,sizeof(dp));dp[0][0]=1;
		for(int i=1;i<=n;i++){
			if(i==t)continue;
			for(int j=i-1;~j;j--)for(int k=0;k<=sum;k++)if(dp[j][k]){
				for(int l=0;l<a[i];l++)
					dp[j+1][k+l]=(dp[j+1][k+l]+1ll*dp[j][k]*c[k+l][l])%MOD;
			}
			sum+=a[i];
		}
		for(int i=0;i<n;i++)for(int j=0;j<=sum;j++)if(dp[i][j]){
			int coef=1ll*dp[i][j]*c[j+a[t]-1][a[t]-1]%MOD*((i&1)?(MOD-1):1)%MOD*pw[i+1][j+a[t]]%MOD;
			res=(res+coef)%MOD;
		}printf("%d ",res);
	}
	return 0;
}