「CSP-S 2019」Emiya 家今天的饭

#3211. 「CSP-S 2019」Emiya 家今天的饭

Summarize

在 $n\times m$ 的矩阵中选 $k$ 个元素,要求 $k\ge 1$,每行最多选 $1$ 个元素,每列元素个数不超过 $\lfloor \frac{k}{2}\rfloor$ 的方案数。

$n\le 100,m\le 2000$

Solution

84 pts

注意到如果某一列不符合条件 3,则其他列必然都符合。

可以先不考虑条件 3,用背包动态规划计算出所有可行方案数。然后枚举不符合条件的列,设其编号为 $c$;记 $f[i][j][k]$ 为在前 $i$ 行中选出 $j$ 个物品,且第 $c$ 列恰好选了 $k$ 个的方案数。

时间复杂度 $O(n^3m)$,可以得到 84 分。

100 pts

优化第二个动态规划,记 $f[i][j]$ 为在前 $i$ 行中,在第 $c$ 列中选出的物品比其他物品个数多 $j$ 的方案数,即可优化一维。

事件复杂度 $O(n^2m)$,可以得到 100 分。

Note

推性质,不能无脑设计状态然后优化。一定要依赖性质。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const ll MOD = 998244353;
const int MAXN = 105;
const int MAXM = 2005;

int n, m;
ll a[MAXN][MAXM], s[MAXN];
ll f[MAXN][MAXN * 2], g[MAXN];

int main() {
freopen("meal.in", "r", stdin);
freopen("meal.out", "w", stdout);
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j) scanf("%lld", &a[i][j]), s[i] = (s[i] + a[i][j]) % MOD;
g[0] = 1;
for (int i = 1; i <= n; ++i)
for (int j = i; j >= 1; --j) g[j] = (g[j] + g[j - 1] * s[i] % MOD) % MOD;
ll ans = 0;
for (int i = 1; i <= n; ++i) ans = (ans + g[i]) % MOD;
for (int c = 1; c <= m; ++c) {
memset(f, 0, sizeof(f));
f[0][n] = 1;
for (int i = 1; i <= n; ++i) {
for (int j = 0; j <= 2 * n; ++j) {
if (j == 0)
f[i][j] = (f[i - 1][j] + f[i - 1][j + 1] * (s[i] - a[i][c] + MOD) % MOD) % MOD;
else
f[i][j] = (f[i - 1][j] + f[i - 1][j + 1] * (s[i] - a[i][c] + MOD) % MOD +
f[i - 1][j - 1] * a[i][c] % MOD) %
MOD;
}
}
for (int i = 1; i <= n; ++i) ans = (ans - f[n][n + i] + MOD) % MOD;
}
cout << ans << endl;

return 0;
}

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×