Emiya家今天的饭

前言

盲猜这是某动漫里的人物

复习一手去年CSP的题,结果发现只会82pts,还是太弱了QAQ(虽然我去年只会32pts

题目

洛谷

讲解

part1 32pts

一个dfs解决,不细讲

part2 64pts

考虑\(dp[i][s1][s2][s3]\)表示前i种烹饪方法,\(m(m<=3)\)种主要食材个选多少个的搭配方案

状态转移方程为:

\[dp[i][s1][s2][s3] = dp[i-1][s1][s2][s3] + dp[i-1][s1-1][s2][s3] * a[i][1] + dp[i-1][s1][s2-1][s3] * a[i][2] + dp[i-1][s1][s2][s3-1] * a[i][3] \]

时间复杂度\(O(n^4)\)

part3 82pts

考虑容斥

先求出总方案数,同样可以使用dp,令\(dp[i][j]\)表示前\(i\)个烹饪方法选\(j\)个的搭配方案数

\[dp[i][j]=dp[i-1][j]+dp[i-1][j-1]*\sum_{k=1}^{m}a[i][k] \]

当然我们可以预处理\(s[i]=\sum_{k=1}^{m}a[i][k]\),总的状态转移方程为:

\[dp[i][j]=dp[i-1][j]+dp[i-1][j-1]*s[i] \]

最终总方案数为:\(\sum_{j=1}^{n}dp[n][j]\)

然后求出不合法的方案数

我们发现,如果一个方案不合法,只会是这种情况:有且仅有一个主要食材超过限制

于是枚举超过限制的主要食材\(p\),令\(f[i][j][k]\)为前\(i\)种烹饪方式选\(j\)种,选到\(k\)个主要食材\(p\)的方案数

\[f[i][j][k] = f[i-1][j][k] + f[i-1][j-1][k] * (s[i] - a[i][p]) + f[i-1][j-1][k-1] * a[i][p] \]

注意做减法的时候有可能减出负数,所以要加一个\(MOD\),即:

\[f[i][j][k] = f[i-1][j][k] + f[i-1][j-1][k] * (s[i] - a[i][p] + MOD) + f[i-1][j-1][k-1] * a[i][p] \]

时间复杂度\(O(n^3m)\)

part4 100pts

我们根据数据范围猜测正解时间复杂度为\(O(n^2m)\)

即我们要优化掉part3中的一维才行

由于我们统计答案的时候只需要考虑\(j/2<k\)的情况,即我们枚举的超过限制的主要食材\(p\)的个数要多于其它所有食材(这个转换很关键)

将它转换为差的形式,\(f[i][j]\)表示前\(i\)个烹饪方式,\(p\)与其它食材的差为\(j\)的方案数

\[f[i][j] = f[i-1][j] + f[i-1][j+1] * (s[i] - a[i][p] + MOD) + f[i-1][j-1] * a[i][p] \]

统计答案的时候只需要统计\(j>0\)的情况就好了

什么,会减到负数,要RE?把第二维所有数字都加上\(n\)就好了!

时间复杂度\(O(n^2m)\)

By the way

dp的第一维明显可以滚动,但是由于出题人没有卡 我懒得写,所以...

代码

int dp[MAXN][MAXN],a[MAXN][MAXM],ans,s[MAXN],f[MAXN][MAXN << 1];

void AM(int &x,LL y)
{
	y %= MOD;
	x += y;
	if(x >= MOD) x -= MOD;
}

int main()
{
//	freopen("meal.in","r",stdin);
//	freopen("meal.out","w",stdout);
	n = Read(); m = Read();
	for(int i = 1;i <= n;++ i)
		for(int j = 1;j <= m;++ j)
			a[i][j] = Read(),AM(s[i],a[i][j]);
	dp[0][0] = 1;
	for(int i = 1;i <= n;++ i)
		for(int j = 0;j <= i;++ j)
		{
			dp[i][j] = dp[i-1][j];
			if(j) AM(dp[i][j],1ll * dp[i-1][j-1] * s[i]);
			if(i == n) AM(ans,dp[i][j]);
		}
	ans--;
	for(int p = 1;p <= m;++ p)
	{
		for(int i = 1;i <= n;++ i)
			for(int j = 0;j <= 2*n;++ j)
				f[i][j] = 0;
		f[0][n] = 1;
		for(int i = 1;i <= n;++ i)
			for(int j = n-i;j <= n+i;++ j)
			{
				f[i][j] = f[i-1][j];
				AM(f[i][j],1ll * f[i-1][j+1] * (s[i] - a[i][p] + MOD));
				if(j-1) AM(f[i][j],1ll * f[i-1][j-1] * a[i][p]);
				if(i == n && j > n) ans = (ans - f[i][j]) % MOD;
			}
	}
	Put((ans + MOD) % MOD);
	return 0;
}
posted @ 2020-08-24 15:13  皮皮刘  阅读(124)  评论(0编辑  收藏  举报