神题,神题啊!!(战术后仰)

题意

给定一棵 nn 个节点的树,点有点权。有 mm 个询问,每次询问规定某两个点选或不选,求最小点覆盖。

题解

概念

做题的第一步是:看懂题意。

  • 点覆盖:在树上选择某些点,使得对于树上的每一条边,都满足两端点至少有一点被选。

  • 最小点覆盖:即选择的点点权和最小的点覆盖。

  • 独立集:在树上选择某些点,使得对于树上的每一条边,都满足两端点没有同时被选。

  • 最大独立集:即选择的点点权最大的独立集。

不难看出,**一棵树上的点覆盖和独立集是一一对应的,一个点覆盖的补集就是一个独立集。**所以,最大独立集是最小点覆盖的补集。(虽然这些东西和题意无关,但还是可以扯一下)

思路

先考虑没有询问时,如何求得最小点覆盖?答案是显然的,直接树形 dp 即可,设 fx,0/1f_{x, 0 / 1} 表示 xx 为根的子树中,xx 选或不选的最小点覆盖权值和,则有:

fx,0=vx.sonfv,1fx,1=vx.sonmin{fv,0,fv,1}+px\begin{aligned} f_{x, 0} &= \sum\limits_{v \in x.\text{son}} f_{v, 1} \\ f_{x, 1} &= \sum\limits_{v \in x.\text{son}} \min\{ f_{v, 0}, f_{v, 1} \} + p_x \end{aligned}

接着可以发现使用这种 dp,可以用动态 dp 来解决此题。但是这里要讲一种常数较小,代码较短的思想。

由于没有修改,所以不用动态 dp,直接使用倍增即可。考虑倍增所使用的状态 hu,i,0/1,0/1h_{u, i, 0 / 1, 0 / 1} (设 ppuu 的第 2i2^i 级祖先),表示从 pp 的子树中抠掉 uu 这棵子树,第一个 0/10 / 1 表示 uu 选还是不选(关于这一维的意义:这里 uu 虽然已经从 pp 中扣掉了,但是在最后回答询问答案时会用到,所以还是要记录一下),第二个表示 pp 选还是不选。

那么有递推式:

hu,i,a,b=mink{0,1}hu,i1,a,k+hfau,i1,i1,k,ba,b{0,1}h_{u, i, a, b} = \min\limits_{k \in \{0, 1\}} h_{u, i - 1, a, k} + h_{fa_{u, i - 1}, i - 1, k, b}\\ a, b \in \{0, 1\}

然后再考虑新添一个辅助数组 gx,0/1g_{x, 0 / 1},表示整颗树扣掉了 xx 为根的子树,且 xx 选或不选的答案。于是有(设 ppxx 父亲):

gx,0=gp,1+fp,1min{fx,0,fx,1}gx,1=min{gx,0,gp,0+fp,0fx,1}\begin{aligned} g_{x, 0} &= g_{p, 1} + f_{p, 1} - \min\{f_{x, 0}, f_{x, 1}\}\\ g_{x, 1} &= \min\{g_{x, 0}, g_{p, 0} + f_{p, 0} - f_{x, 1}\} \end{aligned}

于是在一次询问中,我们可以根据这几个数组求得答案了,具体如下:

这里给了 a,b,x,ya, b, x, y,可以求出 a,ba, bLCALCA。我们可以倍增到 LCALCA 的儿子,求出 aa'bb'(如图所示)。假设跳到 a,ba', b' 的倍增数组分别是 A0/1,0/1,B0/1,0/1A_{0 / 1, 0 / 1}, B_{0 / 1, 0 / 1},答案就是以下两种情况的 min\min

{fa,x+fb,y+gLCA,0+Aa,1+Bb,1fa,x+fb,y+gLCA,1+min{Aa,0,Aa,1}+min{Bb,0,Bb,1}\begin{cases} f_{a, x} + f_{b, y} + g_{LCA, 0} + A_{a, 1} + B_{b, 1} \\ f_{a, x} + f_{b, y} + g_{LCA, 1} + \min\{A_{a, 0}, A_{a, 1}\} + \min\{B_{b, 0}, B_{b, 1}\} \end{cases}

不难看出两种情况的区别就是 LCALCA 选还是不选。注意这里还要加上 LCALCA 的儿子中除了 a,ba', b' 的其它儿子的贡献(图中未画出)

于是这道题就思路就完了。

代码

用时 ****(数据删除)写的:

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#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';
}
}