第十四届蓝桥杯省B.砍树
题目

题目解析及思路
考虑一对无序数对的点 x和 y,如果我们砍掉某条边可以让这两个点不连通 ,那么这条边一定是从 x到 y 路径上的一点 ,我们可以让从 x到 y 路径的边权值都加1 。这个操作我们可以使用树上差分 。 对于 m个无序数对我们都如此操作,最后如果某条边的权值为 m 则说明它符合条件,我们选出符合条件编号最大的那条边就是答案,如果没有权值为 m的边则说明无解。
代码
cpp
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define pii pair<int,int>
#define ms(s,x) memset(s, x, sizeof(s))
using namespace std;
typedef pair<int,int> PII;
const int inf = 0x3f3f3f3f;
const int mod = 1000000007;
const int N = 200010;
int n, m;
vector<int> e[N];
int depth[N], fa[N][16];
int f[N];
int root;
int ans;
map<PII, int> mp;
//lca板子
void bfs(int root){
memset(depth,0x3f,sizeof(depth));
depth[0] = 0,depth[root] = 1;
queue<int> q;
q.push(root);
while(!q.empty()){
int t = q.front();
q.pop();
for(int j:e[t]){
if(depth[j] > depth[t] + 1){
depth[j] = depth[t] + 1;
q.push(j);
fa[j][0] = t;
for(int k=1;k<=15;k++){
fa[j][k] = fa[fa[j][k-1]][k-1];
}
}
}
}
}
int lca(int a,int b){
if(depth[a] < depth[b]) swap(a,b);
for(int k=15;k>=0;k--){
if(depth[fa[a][k]] >= depth[b]){
a = fa[a][k];
}
}
if(a == b) return a;
for(int k=15;k>=0;k--){
if(fa[a][k] != fa[b][k]){
a = fa[a][k];
b = fa[b][k];
}
}
return fa[a][0];
}
//对树上差分数组f进行dfs求和
int dfs(int u,int fa){
int res = f[u];
for(auto v:e[u]){
if(v == fa) continue;
int g = dfs(v,u);
if(g == m){
ans = max(ans,mp[{v,u}]);
}
res += g;
}
return res;
}
signed main(){
cin.tie(0);
cout.tie(0);
ios::sync_with_stdio(false);
ans = 0;
cin>>n>>m;
for(int i=0;i<n-1;i++){
int u,v;
cin>>u>>v;
mp[{u,v}] = mp[{v,u}] = i+1;
e[u].push_back(v);
e[v].push_back(u);
}
//lca
bfs(1);
//树上差分
for(int i=0;i<m;i++){
int u,v;
cin>>u>>v;
int z = lca(u,v);
f[u] ++;
f[v] ++;
f[z] -= 2;
}
dfs(1,-1);
cout << (ans == 0 ? -1 : ans) << '\n';
}