CSP-S 2019 Emiya 家今天的饭
\(\text {Emiya 家今天的饭}\)
Emiya 不会让大家饿肚子,所以将做至少一道菜,即 \(k\geq1\)
Rin 希望品尝不同烹饪方法做出的菜,因此她要求每道菜的烹饪方法互不相同
Yazid 不希望品尝太多同一食材做出的菜,因此他要求每种主要食材至多在一半的菜(即\(⌊k /2⌋\)道菜)中被使用
subtask1 \(\text {期望得分 64pts}\)
\(f[i][j][k][l]\)\(j, k, l\)分别表示每一列选多少数就行了
subtask2 \(\text {期望得分 84pts}\)
考虑到有且只有一列所选的东西会大于\(⌊k /2⌋\)个 即不满足条件
我们可以用容斥定理 用所有情况-不满足条件情况就行了
我们先钦定\(t\) 为选数最多的一列 \(f[i][j][k]\)表示选到了第\(i\)行最多的一行用了\(j\)个 其余列总共选了\(k\)个 答案不满足条件 当切仅当\(j > k\)
转移比较容易
\[f[i][j][k] = f[i - 1][j][k] + f[i - 1][j - 1][k] * a[i][t] + f[i - 1][j][k - 1] *(s[i] - a[i][t])
\]
for (int t = 1; t <= m; t ++ ) {
memset (f, 0, sizeof (f));
f[0][0][0] = 1;
for (int i = 1; i <= n; i ++ ) {
for (int j = 0; j <= i; j ++ ) {
for (int k = 0; k <= i - j; k ++ ) {
f[i][j][k] = f[i - 1][j][k];
if (j) Add (f[i][j][k], f[i - 1][j - 1][k] * a[i][t] % mod);
if (k) Add (f[i][j][k], f[i - 1][j][k - 1] % mod * (s[i] % mod - a[i][t] % mod + mod) % mod);
f[i][j][k] %= mod;
}
}
}
for (int i = 1; i <= n; i ++ )
for (int j = 0; j <= n - i; j ++ )
if (i > j) Add(wrong, f[n][i][j]);
}
时间复杂度\(O(n^3 *m)\)
std \(\text {期望得分 100pts}\)
考虑如何优化\(f[i][j][k]\)可以直接用差值优化一下
\(f[i][j - k]\)表示选到第几行 最多的一行减去其他行的值为\(j - k\)
考虑\(j - k\)可能为负数 所以我们可以集体向右移\(n\) 然后就行了
\[f[i][j] = f[i -1][j] + f[i - 1][j + 1] * a[i][t] + f[i - 1][j - 1]*(s[i]-a[i][t])
\]
int main () {
read (n); read (m);
ll ans = 1;
for (int i = 1; i <= n; i ++ ) for (int j = 1; j <= m; j ++ ) read (a[i][j]), s[i] += a[i][j], s[i] %= mod;
for (int i = 1; i <= n; i ++ ) ans = (ans * (s[i] + 1)) % mod;
for (int t = 1; t <= m; t ++ ) {
memset (f, 0, sizeof (f));
f[0][n] = 1;
for (int i = 1; i <= n; i ++ ) {
for (int j = n - i; j <= n + i; j ++ ) {
Add (f[i][j], (f[i - 1][j] + f[i - 1][j + 1] * (s[i] - a[i][t]) % mod) % mod);
if (j >= 1) Add (f[i][j], f[i - 1][j - 1] * a[i][t] % mod);
}
}
for (int i = n + 1; i <= n * 2; i ++ ) Add (wrong, f[n][i]);
}
write ((ans % mod - wrong % mod + mod - 1 + mod) % mod);
return 0;
}
分析
\(64\)难度不大 考场上心态不炸很好拿 难度普及+
\(84\)分需要分析性质 难度提高
\(84->100\)需要经过思考 难度提高+