首页 > 技术文章 > LGP5664题解

lmpp 2022-02-28 09:50 原文

厉害。

对于每一列选的数最多占一半,我们得设计一个三维 DP。然而状态刚好够,但是转移明显炸了(而且似乎还需要多项式?)

考虑正难则反,DP 不合法的方案数。总方案数很好算。

发现不合法的方案数只有某一列的出现次数超过一半,直接枚举这一列。设当前列为第 \(k\) 列。

\(dp_{i,x,y}\) 为前 \(i\) 行,当前列选了 \(x\) 个,其他列共选了 \(y\) 个的方案数。

再设第 \(i\) 行的 \(a\) 之和为 \(S[i]\),容易有:

\[dp_{i,x,y}=dp_{i-1,x,y}+a[i][k]\times dp_{i-1,x-1,y}+(S[i]-a[i][k])\times dp_{i-1,x,y-1} \]

复杂度 \(O(mn^3)\),可以获得 \(84\) 分。

注意到在统计答案时计算的是 \(\sum_{x>y}dp_{n,x,y}\),我们只关心 \(x\) 是否比 \(y\) 大,考虑重新设状态。

\(dp_{i,x}\) 表示前 \(i\) 行的选择中,当前列比其他列多选了 \(x\) 个(\(x\) 可以为负数)。转移方程和刚才几乎一致:

\[dp_{i,x}=dp_{i-1,x}+a[i][k]\times dp_{i-1,x-1}+(S[i]-a[i][k])\times dp_{i-1,x+1} \]

复杂度 \(O(mn^2)\)

#include<cstdio>
typedef unsigned ui;
const ui M=105,mod=998244353;
ui n,m,S[M],a[M][2005],dp[M][M<<1],f[M][M];
inline ui DP(const ui&x){
	ui sum(0);dp[0][n]=1;
	for(ui i=1;i<=n;++i){
		const ui&c1=a[i][x],&c2=mod+S[i]-a[i][x];
		dp[i][n-i]=1ll*c2*dp[i-1][n-i+1]%mod;dp[i][n+i]=1ll*c1*dp[i-1][n+i-1]%mod;
		for(ui j=n-i+1;j<=n+i-1;++j)dp[i][j]=(dp[i-1][j]+1ull*c1*dp[i-1][j-1]+1ull*c2*dp[i-1][j+1])%mod;
	}
	for(ui i=1;i<=n;++i)sum=(sum+dp[n][n+i])%mod;
	return sum;
}
signed main(){
	ui sum(0);scanf("%d%d",&n,&m);
	for(ui i=1;i<=n;++i)for(ui j=1;j<=m;++j)scanf("%u",a[i]+j),S[i]=(S[i]+a[i][j])%mod;
	for(ui i=1;i<=m;++i)sum=(sum+DP(i))%mod;
	f[0][0]=1;
	for(ui i=1;i<=n;++i){
		f[i][0]=1;
		for(ui j=1;j<=i;++j)f[i][j]=(f[i-1][j]+1ll*S[i]*f[i-1][j-1])%mod;
	}
	for(ui i=1;i<=n;++i)sum=(sum+mod-f[n][i])%mod;
	printf("%u",sum?mod-sum:0);
}

看了题解才会,太菜了/kk

推荐阅读