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;
}