P5024 保卫王国笔记
神题,神题啊!!(战术后仰)
题意
给定一棵
题解
概念
做题的第一步是:看懂题意。
-
点覆盖:在树上选择某些点,使得对于树上的每一条边,都满足两端点至少有一点被选。
-
最小点覆盖:即选择的点点权和最小的点覆盖。
-
独立集:在树上选择某些点,使得对于树上的每一条边,都满足两端点没有同时被选。
-
最大独立集:即选择的点点权最大的独立集。
不难看出,一棵树上的点覆盖和独立集是一一对应的,一个点覆盖的补集就是一个独立集。所以,最大独立集是最小点覆盖的补集。(虽然这些东西和题意无关,但还是可以扯一下)
思路
先考虑没有询问时,如何求得最小点覆盖?答案是显然的,直接树形 dp 即可,设
接着可以发现使用这种 dp,可以用动态 dp 来解决此题。但是这里要讲一种常数较小,代码较短的思想。
由于没有修改,所以不用动态 dp,直接使用倍增即可。考虑倍增所使用的状态
那么有递推式:
然后再考虑新添一个辅助数组
于是在一次询问中,我们可以根据这几个数组求得答案了,具体如下:
这里给了
不难看出两种情况的区别就是
于是这道题就思路就完了。
代码
用时 ****(数据删除)写的:
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
#define int i64
const int N = 1E5 + 5, M = 20, INF = 1E15;
int n, m, p[N]; vector <int> G[N];
int yf[N][M + 1], dep[N];
i64 f[N][2], h[N][M + 1][2][2], g[N][2];
void dfs(int x, int fa) {
f[x][0] = 0; f[x][1] = p[x];
for (auto v : G[x]) {
if (v == fa) continue;
yf[v][0] = x; dep[v] = dep[x] + 1;
for (int i = 1; i <= M; ++i)
yf[v][i] = yf[yf[v][i - 1]][i - 1];
dfs(v, x);
f[x][0] += f[v][1];
f[x][1] += min(f[v][0], f[v][1]);
}
}
void dfs1(int x, int fa) {
for (auto v : G[x]) {
if (v == fa) continue;
h[v][0][0][0] = INF;
h[v][0][0][1] = f[x][1] - min(f[v][0], f[v][1]);
h[v][0][1][0] = f[x][0] - f[v][1];
h[v][0][1][1] = f[x][1] - min(f[v][0], f[v][1]);
for (int i = 1; i <= M; ++i) {
for (int a : {0, 1}) for (int b : {0, 1}) {
h[v][i][a][b] = INF;
for (int k : {0, 1})
h[v][i][a][b] = min(h[v][i][a][b],
h[v][i - 1][a][k] + h[yf[v][i - 1]][i - 1][k][b]);
}
}
g[v][0] = g[x][1] + f[x][1] - min(f[v][0], f[v][1]);
g[v][1] = min(g[v][0], g[x][0] + f[x][0] - f[v][1]);
dfs1(v, x);
}
}
int solve(int a, int x, int b, int y) {
if (dep[a] < dep[b]) swap(a, b), swap(x, y);
int A[2] = {INF, INF}, B[2] = {INF, INF};
A[x] = f[a][x]; B[y] = f[b][y];
for (int i = M; ~i; --i) {
if (dep[a] - (1 << i) >= dep[b]) {
int pA[] = {A[0], A[1]};
A[0] = min(pA[1] + h[a][i][1][0], pA[0] + h[a][i][0][0]);
A[1] = min(pA[1] + h[a][i][1][1], pA[0] + h[a][i][0][1]);
a = yf[a][i];
}
}
if (a == b) return g[b][y] + A[y];
else {
for (int i = M; ~i; --i) {
if (yf[a][i] != yf[b][i]) {
int pA[] = {A[0], A[1]}, pB[] = {B[0], B[1]};
A[0] = min(pA[1] + h[a][i][1][0], pA[0] + h[a][i][0][0]);
A[1] = min(pA[1] + h[a][i][1][1], pA[0] + h[a][i][0][1]);
B[0] = min(pB[1] + h[b][i][1][0], pB[0] + h[b][i][0][0]);
B[1] = min(pB[1] + h[b][i][1][1], pB[0] + h[b][i][0][1]);
a = yf[a][i]; b = yf[b][i];
}
}
int lca = yf[a][0];
int ans0 = f[lca][0] - f[a][1] - f[b][1] + A[1] + B[1] + g[lca][0];
int ans1 = f[lca][1] - min(f[a][0], f[a][1]) - min(f[b][0], f[b][1]) + min(A[1], A[0]) + min(B[1], B[0]) + g[lca][1];
return min(ans0, ans1);
}
}
signed main(void) {
ios :: sync_with_stdio(false);
cin.tie(nullptr); cout.tie(nullptr);
cin >> n >> m; string typp; cin >> typp; typp = "";
for (int i = 1; i <= n; ++i) cin >> p[i];
for (int i = 1; i < n; ++i) {
int u, v; cin >> u >> v;
G[u].emplace_back(v);
G[v].emplace_back(u);
} dep[1] = 1; for (int i = 0; i <= M; ++i) yf[1][i] = 1;
dfs(1, 0); dfs1(1, 0);
map <pair <int, int>, bool> vis;
function <void(int, int)> prep = [&](int x, int fa) {
for (auto v : G[x]) {
if (v == fa) continue;
vis[make_pair(v, x)] = vis[make_pair(x, v)] = 1;
prep(v, x);
}
} ; prep(1, 0);
for (int i = 1; i <= m; ++i) {
int a, x, b, y; cin >> a >> x >> b >> y;
if (!x && !y && vis.count(make_pair(a, b))) {
cout << "-1\n";
continue;
}
cout << solve(a, x, b, y) << '\n';
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具