题目大意
给你一棵有 n n n个点的树以及 q q q组询问,每次询问给定 x , w x,w x,w,求任意一个离点 x x x的距离为 w w w的点 y y y。如果点 y y y不存在,则输出 − 1 -1 −1。
2 ≤ n ≤ 2 × 1 0 5 , 1 ≤ q ≤ 2 × 1 0 5 2\leq n\leq 2\times 10^5,1\leq q\leq 2\times 10^5 2≤n≤2×105,1≤q≤2×105
题解
首先, O ( n ) O(n) O(n)求出树的直径,设这条直径的两个端点为 v x vx vx和 v y vy vy。
对于每一组询问 x , w x,w x,w,如果 x x x到 v x vx vx的距离或 x x x到 v y vy vy的距离大于等于 w w w,则在其路径上查找点 y y y,否则输出 − 1 -1 −1。
为什么可以这样呢?因为树上的任意一个点到树的直径的两个端点的距离的最大值大于等于这个点到树上任意一个点的距离。
证明: 假设存在一个点 x x x,点 x x x到点 y y y的距离大于点 x x x到树的直径的两个端点的距离的最大值,分两种情况讨论:
- 若 x x x到 y y y的路径与树的直径没有交集,则 x x x到 y y y的路径比树的直径长,不满足树的直径的定义,矛盾
- 若 x x x到 y y y的路径与数的直径交于两点 u u u和 v v v( u u u靠近 x x x, v v v靠近 y y y),设树的直径上靠近 u u u的端点为 v x vx vx,靠近 v v v的端点为 v y vy vy。依照假设,路径 x − u − v − y x-u-v-y x−u−v−y比路径 x − u − v − v y x-u-v-vy x−u−v−vy长,也就是路径 v − y v-y v−y比路径 v − v y v-vy v−vy长,那么路径 v x − u − v − y vx-u-v-y vx−u−v−y比路径 v x − u − v − v y vx-u-v-vy vx−u−v−vy长,也就是比直径长,不满足树的直径的定义,矛盾
由此即可得证。
可以自己画图方便理解。
假设 x x x到 v x vx vx的距离大于等于 w w w,设 x x x和 v x vx vx的 l c a lca lca为 z z z。
- 如果 d e p x − d e p z ≥ w dep_x-dep_z\geq w depx−depz≥w,则在 x x x到根的路径上二分查找 y y y
- 如果 d e p x − d e p z > w dep_x-dep_z>w depx−depz>w,则在 v x vx vx到根的路径上二分查找 y y y
求 l c a lca lca可以用倍增法,这样方便二分求 y y y。
时间复杂度为 O ( n log n ) O(n\log n) O(nlogn)。
code
#include<bits/stdc++.h>
using namespace std;
const int N=200000;
int n,m,mx,vx,vy,tot=0,d[2*N+5],l[2*N+5],r[2*N+5],dep[N+5],f[N+5][20];
void add(int xx,int yy){
l[++tot]=r[xx];d[tot]=yy;r[xx]=tot;
}
void dfs1(int u,int fa,int &v){
dep[u]=dep[fa]+1;
if(dep[u]>mx){
v=u;mx=dep[u];
}
for(int i=r[u];i;i=l[i]){
if(d[i]==fa) continue;
dfs1(d[i],u,v);
}
}
void dfs2(int u,int fa){
dep[u]=dep[fa]+1;
f[u][0]=fa;
for(int i=1;i<=19;i++){
f[u][i]=f[f[u][i-1]][i-1];
}
for(int i=r[u];i;i=l[i]){
if(d[i]==fa) continue;
dfs2(d[i],u);
}
}
int gtlca(int x,int y){
if(dep[x]<dep[y]) swap(x,y);
for(int i=19;i>=0;i--){
if(dep[f[x][i]]>=dep[y]) x=f[x][i];
}
if(x==y) return x;
for(int i=19;i>=0;i--){
if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
}
return f[x][0];
}
bool check(int x,int y,int w){
int lca=gtlca(x,y);
if(dep[x]+dep[y]-2*dep[lca]<w) return 0;
if(dep[x]-dep[lca]>=w){
for(int i=19;i>=0;i--)
if(w>=(1<<i)) w-=(1<<i),x=f[x][i];
printf("%d\n",x);
}
else{
w=dep[x]+dep[y]-2*dep[lca]-w;
for(int i=19;i>=0;i--)
if(w>=(1<<i)) w-=(1<<i),y=f[y][i];
printf("%d\n",y);
}
return 1;
}
int main()
{
// freopen("intoxicated.in","r",stdin);
// freopen("intoxicated.out","w",stdout);
scanf("%d",&n);
for(int i=1,x,y;i<n;i++){
scanf("%d%d",&x,&y);
add(x,y);add(y,x);
}
mx=0;dfs1(1,0,vx);
mx=0;dfs1(vx,0,vy);
dfs2(1,0);
scanf("%d",&m);
for(int o=1,x,w;o<=m;o++){
scanf("%d%d",&x,&w);
if(check(x,vx,w)) continue;
if(check(x,vy,w)) continue;
printf("-1\n");
}
return 0;
}