ABC267F Exactly K Steps
洛谷[ABC267F] Exactly K Steps
给你一棵有 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到 v x vx vx的距离大于等于 w w w,设 x x x和 v x vx vx的 l c a lca lca为 z z z。
求 l c a lca lca可以用倍增法,这样方便二分求 y y y。
时间复杂度为 O ( n log n ) O(n\log n) O(nlogn)。
#include
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;
}