题意
给一张无向连通图和图上的一棵生成树。问至少删除几条边使图不连通,且满足删除的边有且仅有一条树边。
题解
若删除一条树边\((u, v)\),那么\(u\)和\(v\)之间的非树边都要删除,这样就是一种方案。
如何计算\(u\)与\(v\)之间的非树边呢?考虑对所有非树边,做树上差分\(cnt[u_i]\)++, \(cnt[v_i]\)++, \(cnt[lca(u_i, v_i)]\) -= \(2\)。那么若\(v\)为\(u\)儿子节点,那么以\(v\)为根节点的子树\(cnt\)和就是答案。
故枚举所有的非树边\((u, v)\),更新答案最小值。最后答案不要忘了加上那一条树边。
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e4 + 100;
int T, n, m, x, y, ans;
vector<int> V[maxn];
int cnt[maxn], dep[maxn];
int r[maxn][20];
int depth;
void build(int x, int y) {
V[x].push_back(y);
V[y].push_back(x);
}
int get_ans(int x, int from) {
int res = cnt[x];
for (auto y : V[x]) {
if (y == from) continue;
res += get_ans(y, x);
}
if (x != 1) ans = min(ans, res);
return res;
}
void init_lca(int root=1) {
queue<int> Q;
Q.push(root);
dep[root] = 1;
while(!Q.empty()) {
int x = Q.front(); Q.pop();
for (auto y : V[x]) {
if (dep[y]) continue;
dep[y] = dep[x] + 1;
r[y][0] = x;
for (int j = 1; j <= depth; j++)
r[y][j] = r[r[y][j-1]][j-1];
Q.push(y);
}
}
}
int lca(int x, int y) {
if (dep[x] > dep[y]) swap(x, y);
for (int i = depth; i >= 0; i--)
if (dep[r[y][i]] >= dep[x]) y = r[y][i];
if (x == y) return x;
for (int i = depth; i >= 0; i--)
if (r[x][i] != r[y][i]) x = r[x][i], y = r[y][i];
return r[x][0];
}
int main() {
scanf("%d", &T);
for (int ca = 1; ca <= T; ca++) {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) V[i].clear();
memset(cnt, 0, sizeof(cnt));
for (int i = 1; i <= n-1; i++) {
scanf("%d%d", &x, &y);
build(x, y);
}
depth = (int)(log(n)/log(2)) + 1;
init_lca();
for (int i = n; i <= m; i++) {
scanf("%d%d", &x, &y);
int L = lca(x, y);
cnt[x]++, cnt[y]++, cnt[L] -= 2;
}
ans = 0x3f3f3f3f;
get_ans(1, 0);
printf("Case #%d: %d\n", ca, ans+1);
}
}