Luogu P5664 Emiya 家今天的饭

重做下去年的CSP题找找感觉,去年D1T2写可持久化线段树上二分的悲惨经历让我对D1T2充满了厌恶(好吧其实是正解在简单都懒得写了),因此就来改这个去年没调出来的DP了

首先这个主要食材占一半一眼容斥,因此我们大体思路就有了

先求出不管这个限制的总方案数,设\(f_{i,j}\)表示前\(i\)种方法中做了\(j\)道菜的方案数,记录一个每行的和显然可以\(O(n^2)\)转移

考虑大力枚举哪个食材\(p\)超过了\(\lfloor \frac{k}{2}\rfloor\),显然可以再做一次DP,设\(g_{i,j,k}\)表示前\(i\)种方法中做了\(j\)道菜的方案数,其中有\(k\)道菜使用了食材\(p\),转移显然

但这样总复杂度是\(O(n^3m)\)无法通过,放一下代码意思一下

#include<cstdio>
#include<cstring>
#define RI register int
#define CI const int&
using namespace std;
const int N=105,M=2005,mod=998244353;
int n,m,a[N][M],f[N][N],g[N][N][N],sum[N],ans;
int main()
{
	RI i,j,k,p; for (scanf("%d%d",&n,&m),i=1;i<=n;++i)
	for (j=1;j<=m;++j) scanf("%d",&a[i][j]),(sum[i]+=a[i][j])%=mod;
	for (f[0][0]=1,i=1;i<=n;++i) for (j=0;j<=i;++j)
	f[i][j]=(f[i-1][j]+(j?1LL*f[i-1][j-1]*sum[i]%mod:0))%mod;
	for (i=1;i<=n;++i) (ans+=f[n][i])%=mod;
	for (p=1;p<=m;++p)
	{
		for (g[0][0][0]=i=1;i<=n;++i) for (j=0;j<=i;++j) for (k=0;k<=j;++k)
		g[i][j][k]=(1LL*g[i-1][j][k]+(j?1LL*g[i-1][j-1][k]:0)*(sum[i]-a[i][p]+mod)%mod+
		(j&&k?1LL*g[i-1][j-1][k-1]*a[i][p]%mod:0))%mod; 
		for (i=1;i<=n;++i) for (j=(i>>1)+1;j<=n;++j) (ans+=mod-g[n][i][j])%=mod;
	}
	return printf("%d",ans),0;
}

我们仔细观察一下转移方程发现我们只需要知道后两维的差即可(话说这一年做的AGC中有太多这样的套路了),因此直接压成一维总复杂度就是\(O(n^2m)\)的了

#include<cstdio>
#include<cstring>
#define RI register int
#define CI const int&
using namespace std;
const int N=105,M=2005,mod=998244353;
int n,m,a[N][M],f[N][N],g[N][N<<1],sum[N],ans;
int main()
{
	RI i,j,p; for (scanf("%d%d",&n,&m),i=1;i<=n;++i)
	for (j=1;j<=m;++j) scanf("%d",&a[i][j]),(sum[i]+=a[i][j])%=mod;
	for (f[0][0]=1,i=1;i<=n;++i) for (j=0;j<=i;++j)
	f[i][j]=(f[i-1][j]+(j?1LL*f[i-1][j-1]*sum[i]%mod:0))%mod;
	for (i=1;i<=n;++i) (ans+=f[n][i])%=mod;
	for (p=1;p<=m;++p)
	{
		for (g[0][n]=i=1;i<=n;++i) for (j=-i;j<=i;++j)
		g[i][n+j]=(1LL*g[i-1][n+j]+1LL*g[i-1][n+j-1]*a[i][p]%mod+
		1LL*g[i-1][n+j+1]*(sum[i]-a[i][p]+mod)%mod)%mod;
		for (i=1;i<=n;++i) (ans+=mod-g[n][n+i])%=mod;
	}
	return printf("%d",ans),0;
}
posted @ 2020-11-04 20:12  空気力学の詩  阅读(103)  评论(1编辑  收藏  举报