题解:P12002 吃猫粮的玉桂狗
又拿这个套路炒了一波冷饭。看赛时榜好像大家还是不太会,所以希望这个题能对这个套路有一定普及作用。
猜你想搜:
- P5664 Emiya 家今天的饭
- P8202 染色
- CF1487G String Counting
考虑这个每种猫粮的数量多于一半有什么用:在满足位置限制(所有的
可以考虑枚举那个超限制的猫粮
注意到我们关于位置的限制(所有的
枚举
这里当
如果不理解树上背包转移可以去看代码。
如果把第二维拿掉,那么这个 dp 是在做标准树上背包。所以上式是在树上背包的基础上枚举了
那么
对
减一下就做完了。
注意树上 DP 的过程里不要对最后一维有无效的枚举,否则复杂度是不对的。
#include <bits/stdc++.h>
const int p = 353'442'899;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n, m, t;
std::cin >> n >> m >> t;
std::vector<int> c(m + 1);
for (int i = 1; i <= m; ++i) std::cin >> c[i];
std::vector e(n + 1, std::vector<int>()), lim(m + 1, std::vector<int>(m + 1, 0));
for (int i = 1, u, v; i < n; ++i) {
std::cin >> u >> v;
e[u].push_back(v);
e[v].push_back(u);
}
for (int u, v; t; --t) {
std::cin >> u >> v;
lim[u][v] = 1;
}
std::vector g(n + 1, std::vector(m + 1, 0));
auto Dfs = [&](auto &&dfs, int u, int pre) -> void {
for (int col = 1; col <= n; ++col) g[u][col] = 1;
for (auto v : e[u]) if (v != pre) {
dfs(dfs, v, u);
for (int curCol = 1; curCol <= m; ++curCol) {
int sum = 0;
for (int childCol = 1; childCol <= m; ++childCol) if (!lim[curCol][childCol]) {
(sum += g[v][childCol]) %= p;
}
g[u][curCol] = 1ll * g[u][curCol] * sum % p;
}
}
};
Dfs(Dfs, 1, 0);
int ans = 0;
for (int col = 1; col <= m; ++col) {
(ans += g[1][col]) %= p;
}
for (int tarCol = 1; tarCol <= m; ++tarCol) {
std::vector f(n + 1, std::vector(m + 1, std::vector(n + 1, 0)));
std::vector<int> sz(n + 1);
auto dfs = [&](auto &&dfs, int u, int pre) -> void {
sz[u] = 1;
for (int curCol = 1; curCol <= m; ++curCol) {
f[u][curCol][curCol == tarCol] = 1;
}
for (auto v : e[u]) if (v != pre) {
dfs(dfs, v, u);
for (int curCol = 1; curCol <= m; ++curCol) {
std::vector curf(sz[u] + sz[v] + 1, 0);
for (int childCol = 1; childCol <= m; ++childCol) if (!lim[curCol][childCol]) {
for (int lsh = 0; lsh <= sz[u]; ++lsh) {
for (int rsh = 0; rsh <= sz[v]; ++rsh) {
curf[lsh + rsh] += 1ll * f[v][childCol][rsh] * f[u][curCol][lsh] % p;
curf[lsh +rsh] %= p;
}
}
}
for (int i = 0; i <= sz[u] + sz[v]; ++i) f[u][curCol][i] = curf[i];
}
sz[u] += sz[v];
}
};
dfs(dfs, 1, 0);
for (int i = c[tarCol] + 1; i <= n; ++i) {
for (int col = 1; col <= m; ++col) {
ans -= f[1][col][i];
ans = (ans + p) % p;
}
}
}
std::cout << ans << std::endl;
}
不知道下次把这个精品小套路掏出来扔到比赛里会是什么时间。