前言
盲猜这是某动漫里的人物
复习一手去年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;
}