题目链接:http://acm.split.hdu.edu.cn/showproblem.php?pid=5452
解法:首先思路肯定是要枚举树上删掉的一条边,然后就转化为求树上分开的两块之间连的其它边的条数。我们看非树边(u,v),如果删除的边在lca(u,v)和u,v之间,那么这条边就会起到贡献,为1,如果删除的边在u,v的子树下,则没有起到贡献,同理如果删除的边在lca(u,v)上面的话,也不会起到作用,那么我们就可以给点打上tag,lca(u,v)处减2,u与v处加1,那么可以统计子树上点的val就可以知道其它边的条数(可以手动验证)。那么打完tag写个深搜统计一下即可。
AC代码:
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <vector>
using namespace std;
int T;
const int N = 20000 + 5;
int n,m;
const int M = 200000 + 5;
int u[M],v[M];
vector<int> G[N];
int root;
int par[20][N];
int depth[N];
void dfs(int v,int p,int d){
par[0][v] = p;
depth[v] = d;
for(int i = 0;i < (int) G[v].size();i++){
int u = G[v][i];
if(u != p) dfs(u,v,d + 1);
}
}
void init(int V){
dfs(root,-1,0);
for(int k = 0;k + 1 < 20;k++){
for(int v = 0;v < V;v++){
if(par[k][v] < 0) par[k + 1][v] = -1;
else par[k + 1][v] = par[k][par[k][v]];
}
}
}
int lca(int u,int v){
if(depth[u] > depth[v]) swap(u,v);
for(int k = 0;k < 20;k++){
if((depth[v] - depth[u]) >> k & 1){
v = par[k][v];
}
}
if(u == v) return u;
for(int k = 19;k >= 0;k--){
if(par[k][u] != par[k][v]){
u = par[k][u];
v = par[k][v];
}
}
return par[0][u];
}
int val[N];
int sum[N];
void solve(int u,int p){
sum[u] = val[u];
for(int i = 0;i < (int) G[u].size();i++){
int v = G[u][i];
if(v == p) continue;
solve(v,u);
sum[u] += sum[v];
}
}
int main(){
cin >> T;
int cas = 0;
while(T--){
printf("Case #%d: ",++cas);
memset(val,0,sizeof(val));
memset(sum,0,sizeof(sum));
cin >> n >> m;
for(int i = 0;i < n;i++) G[i].clear();
for(int i = 1;i <= m;i++){
scanf("%d%d",u + i,v + i);
}
root = 0;
for(int i = 1;i < n;i++){
G[u[i] - 1].push_back(v[i] - 1);
G[v[i] - 1].push_back(u[i] - 1);
}
init(n);
for(int i = n;i <= m;i++){
int fa = lca(u[i] - 1,v[i] - 1);
val[fa] -= 2;
val[u[i] - 1]++;
val[v[i] - 1]++;
}
solve(root,-1);
int ans = 1e9 + 7;
for(int i = 0;i < n;i++){
if(i == root) continue;
else ans = min(ans,sum[i]);
}
cout << ans + 1 << endl;
}
return 0;
}