题意:给一棵树,m次查询, 每次查询给两个集合, 从这两个集合里分别选一个结点,使得这两个节点的lca的深度最大。
考虑dfs序为5,6,7的三个节点,5,6的lca深度一定大于等于5,7的lca深度
所以可以讲查询集合按dfs序排序,然后类似尺取的操作,使得每一对dfs序接近的节点都做一次lca,选一个最深的就可以了
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
vector<int>w[maxn];
int dep[maxn], fa[maxn], st[maxn], up[maxn][21], n, tot;
void init(){
for(int i = 1; i <= n; i++)
up[i][0] = fa[i];
for(int i = 1; i <= 20; i++)
for(int j = 1; j <= n; j++)
up[j][i] = up[up[j][i - 1]][i - 1];
return ;
}
int lca(int u, int v){
if(dep[u] < dep[v])
swap(u, v);
int dis = dep[u] - dep[v];
for(int i = 0; i <= 20; i++){
if(dis & (1 << i)){
u = up[u][i];
}
}
if(u == v)
return u;
for(int i = 20; i >= 0; i--){
if(up[u][i] != up[v][i]){
u = up[u][i];
v = up[v][i];
}
}
return fa[u];
}
void dfs(int f, int x){
st[x] = tot++;
fa[x] = f;
dep[x] = dep[f] + 1;
for(int i = 0; i < w[x].size(); i++){
if(w[x][i] != f){
dfs(x, w[x][i]);
}
}
return ;
}
int s1[maxn], s2[maxn];
int cmp(int u, int v){
return st[u] < st[v];
}
int main(){
int m, u, v, k1, k2;
while(cin >> n >> m){
for(int i = 1; i <= n; i++)
w[i].clear();
for(int i = 1; i < n; i++){
scanf("%d %d", &u, &v);
w[u].push_back(v);
w[v].push_back(u);
}
tot = 0;
dfs(0, 1);
init();
while(m--){
int ans = 0;
scanf("%d", &k1);
for(int i = 0; i < k1; i++)
scanf("%d", &s1[i]);
scanf("%d", &k2);
for(int i = 0; i < k2; i++)
scanf("%d", &s2[i]);
sort(s1, s1 + k1, cmp);
sort(s2, s2 + k2, cmp);
int i1 = 0, i2 = 0;
while(i1 < k1 && i2 < k2){
ans = max(ans, dep[lca(s1[i1], s2[i2])]);
// cout << s1[i1] << " " << s2[i2] << endl;
// cout << lca(s1[i1], s2[i2]) << endl;
if(st[s1[i1]] > st[s2[i2]])
i2++;
else
i1++;
}
printf("%d\n", ans);
}
}
return 0;
}
还有一种做法,复杂度比这个高,二分深度,然后把第一个集合所有在此深度的父节点存到set或map里,在判断第二个集合所有节点有没有相同的此深度的父节点。