[CSP-S2019] Emiya 家今天的饭 题解

Statement

Emiya 家今天的饭 - 洛谷

Solve

可以发现最难处理是第三个限制,即对列的限制

正难则反 我们考虑容斥

\(ans=\) 总方案数-有一列不合法方案书

显然,只可能有一列不合法

——

首先考虑总方案数怎么算

\(f[i][j]\) 表示前 \(i\) 行选 \(j\) 个菜的方案数,那么

\[f[i][j]=f[i-1][j]+f[i-1][j-1]*sum[i] \]

其中,\(sum[i]=\sum_{j=1}^ma[i][j]\)

那么总方案数为 \(\sum_{i=1}^n f[n][i]\) ,初态 \(f[0][0]=1\) 即可

这样的时间是 \(O(nm)\) ,足够了

更快的方法是,\(all=\prod (sum[i]+1)-1\) ,减一是因为不能不选

——

因为只有一列不合法,我们考虑枚举这个列,设它为 \(pos\)

考虑状态怎么设,发现我们只关心 \(pos\) 列选的数量和其他列选的数量,

因此,设 \(g[i][j][k]\) 表示前 \(i\) 行,第 \(pos\) 列选了 \(j\) 次,其他列选了 \(k\) 次的方案数,那么

\[g[i][j][k]=g[i-1][j][k]+g[i-1][j-1][k]*a[i][pos]+g[i-1][j][k-1]*(sum[i]-a[i][j]) \]

即不选/选\(pos\)/选其他

总共不合法方案为 \(\sum_{j>k}g[n][j][k]\)

但这样时间是 \(O(n^3m)\) ,需要优化

由上述计算总不合法的式子,发现我们真正关心的是 \(j,k\) 的大小关系

于是我们可以更改状态,设 \(g[i][j]\) 表示前 \(i\) 行,第 \(pos\) 列比其他列多选了 \(j\) 次的方案数,那么

\[g[i][j]=g[i-1][j]+g[i-1][j-1]*a[i][pos]+g[i-1][j+1]*(sum[i]-a[i][pos]) \]

即不选/选 \(pos\)/选其他

这样,时间就是 \(O(n^2m)\)

同时考虑到 \(j\) 可能为负数,所以我们不妨将他整体加上 \(n\)

Code

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod = 998244353;
const int N = 1e2+5;
const int M = 2e3+5;

int a[N][M],f[N][N],g[N][N*2],sum[N];
int n,m,ans;

signed main(){
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<=n;++i)
		for(int j=1;j<=m;++j)
			scanf("%lld",&a[i][j]),(sum[i]+=a[i][j])%=mod;
	f[0][0]=1;
	for(int i=1;i<=n;++i)
		for(int j=0;j<=n;++j)
			f[i][j]=(f[i-1][j]+(j?f[i-1][j-1]*sum[i]%mod:0))%mod;
	for(int i=1;i<=n;++i)
		(ans+=f[n][i])%=mod;
	for(int pos=1;pos<=m;++pos){
		memset(g,0,sizeof(g));
		g[0][n]=1;
		for(int i=1;i<=n;++i)
			for(int j=n-i;j<=n+i;++j)
				g[i][j]=(g[i-1][j]+g[i-1][j-1]*a[i][pos]%mod+g[i-1][j+1]*(sum[i]-a[i][pos])%mod)%mod;
		for(int i=1;i<=n;++i)
			ans=(ans-g[n][i+n]+mod)%mod;
	}
	printf("%lld\n",ans%mod);
	return 0;
}
posted @ 2021-08-18 09:06  _Famiglistimo  阅读(35)  评论(0编辑  收藏  举报