传送门
就是简单的ST表问题+ LCA,用ST表预处理好连续区间的LCA,然后进行查询
注意HDU是多组输入,把相应的值清零即可。
LCA采用倍增法,lg是常数优化。
ST表用模板,维护([i,i + 2^j - 1])这个区间的LCA
时间复杂度为预处理(O(nlogn))
查询(O(mlogn))
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
using namespace std;
const int N = 3e5 + 5;
int st[N][22];
struct Edge{
int to, next;
}e[N << 1];
int head[N], tot;
void add(int u, int v){
e[++tot].to = v;
e[tot].next = head[u];
head[u] = tot;
}
int depth[N], fa[N][22], lg[N];
void dfs(int u, int fath) {//初始化fa[u][i]与depth[u]
fa[u][0] = fath, depth[u] = depth[fath] + 1;
for(int i = 1; i <= lg[depth[u]]; i++)//定理
fa[u][i] = fa[fa[u][i - 1]][i - 1];
for(int i = head[u]; i; i = e[i].next){
int v = e[i].to;
if(v != fath)
dfs(v, u);
}
}
int LCA(int x, int y) {//求两个点的最近公共祖先
if(depth[x] < depth[y]) swap(x, y);
while(depth[x] > depth[y])
x = fa[x][lg[depth[x] - depth[y]] - 1];//到同一深度
if(x == y) return x;
for(int k = lg[depth[x]] - 1; k >= 0; k--)//向上跳,一直到LCA的下一个结点
if(fa[x][k] != fa[y][k])
x = fa[x][k], y = fa[y][k];
return fa[x][0];
}
int Query(int l, int r){
int k = log2(r - l + 1);
return LCA(st[l][k], st[r - (1 << k) + 1][k]);
}
int main(){
int n, m;
while(~scanf("%d", &n)){
memset(depth, 0, sizeof(depth));
memset(head, 0, sizeof(head));
memset(fa, 0, sizeof(fa));
tot = 0;
for(int i = 1; i < n; i++){
int u, v;
scanf("%d%d", &u, &v);
add(u, v); add(v, u);
}
for(int i = 1; i <= n; i++){
st[i][0] = i;
lg[i] = lg[i - 1] + (1 << lg[i - 1] == i);
}
dfs(1, 0);
for(int j = 1; j <= log2(n / 2) + 1; j++)
for(int i = 1; i + (1 << j) - 1 <= n; i++)
st[i][j] = LCA(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]);
scanf("%d", &m);
for(int i = 1; i <= m; i++){
int l, r;
scanf("%d%d", &l, &r);
printf("%d
", Query(l, r));
}
}
return 0;
}