简要题意:给出一个 n+1 个点的树,以及若干个点对,需要断开一些点,使得这些点对路径不连通。输出应该断开的最少点数。
我们断开一个点,能够影响到的是:
1.
子树中过这个点的路径.
2.
一个点在子树中,另一个点在祖先中的路径。
为了使得以上两个影响尽可能的大,我们每次需要使得断开的点的子树尽可能大。
因此当我们打算断开一对点对
(u,v)
的时候,为了使得断开的点的影响尽可能大,我们需要断开路径上深度最小的那个点,也就是
lca(u,v)
。
①. 如果一对点对
(u,v)
的其中一个点在某个被断开的点的子树中,而另一个点不在,则说明这两点间的路径经过被断开点,那么这条路径就没有再次断开的必要了。
②. 如果一对点对两个点都不在任何已经被断开的点的子树中,则说明这两个点的
lca
还需要被断开。
事实上还有一种情况, 如果两个点都被覆盖了,则可能出现的情况是,两个点之间的路径都被覆盖了,但是却没有一个点被断开。对于这种情况我们也需要将两点间的
lca
断开。但是我们断开这次
lca
之后,有可能会导致之前我们处理过的,本该被判定为情况①,因为处理顺序的缘故被判定为情况②,从而导致结果偏大。因此为了避免这种情况,需要离线询问,按
lca
的深度从大到小来处理。
判定一个点是否在某个被断开的点的子树中,只需要看这个点是否被标记过。
每次断开一个点,都需要把这个点的子树里的点都标记上。对于这种对子树的简单操作,一般我们都使用dfs序,更麻烦一些的,使用树链剖分。
使用了dfs序之后,给子树标记可以使用线段树。不过我们只需要知道一个点是否被标记过,因此使用树状数组即可。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 110000;
int p[maxn][20], deep[maxn], L[maxn], R[maxn], cid;
vector<int> G[maxn];
void dfs(int u, int fa) {
p[u][0] = fa;
L[u] = ++cid;
deep[u] = deep[fa] + 1;
for(auto v : G[u]) {
if(v == fa) continue;
dfs(v, u);
}
R[u] = cid;
}
int goup(int x, int len) {
for(int i = 0; i < 20; i++) {
if(len & (1 << i) && x)
x = p[x][i];
}
return x;
}
int lca(int u, int v) {
if(deep[u] < deep[v]) swap(u, v);
int d = deep[u] - deep[v];
u = goup(u, d);
if(v == u) return u;
for(int i = 19; i >= 0; i--) {
if(p[u][i] == p[v][i]) continue;
u = p[u][i];
v = p[v][i];
}
return p[u][0];
}
namespace BIT {
int C[maxn];
void init() {
memset(C, 0, sizeof C);
}
int lowbit(int x) {
return x & -x;
}
void ins(int x, int v) {
for(int i = x; i < maxn; i+=lowbit(i))
C[i] += v;
}
int ask(int x) {
int res = 0;
for(int i = x; i; i-=lowbit(i))
res += C[i];
return res;
}
}
struct A {
int u, v, lc, lcdep;
bool operator < (const A & b) const {
return lcdep > b.lcdep;
}
};
void init() {
for(int i = 0; i < maxn; i++) G[i].clear();
deep[0] = 0;
cid = 0;
}
int main() {
int n;
while(~scanf("%d", &n)) {
init();
for(int i = 1; i <= n; i++) {
int x, y;
scanf("%d%d", &x, &y);
x ++, y++;
G[x].push_back(y);
G[y].push_back(x);
}
n++;
dfs(1, 0);
for(int i = 1; i < 20; i++) {
for(int j = 1; j <= n; j++) {
if(p[j][i-1] == 0) p[j][i] = 0;
else p[j][i] = p[p[j][i-1]][i-1];
}
}
int q;
scanf("%d", &q);
vector<A> V;
while(q--) {
int x, y;
scanf("%d%d", &x, &y);
x++, y++;
int lc = lca(x, y);
V.push_back({x, y, lc, deep[lc]});
}
sort(V.begin(), V.end());
int ans = 0;
BIT::init();
for(int i = 0; i < V.size(); i++) {
int u = V[i].u, v = V[i].v, lc = V[i].lc;
if(BIT::ask(L[u]) || BIT::ask(L[v])) continue;
BIT::ins(L[lc], 1);BIT::ins(R[lc] + 1, -1); ans++;
}
printf("%d\n", ans);
}
return 0;
}