题意:
首先给一棵 n 个点的树,然后有 q 次的询问,每次询问会向树上点 x 和 y 之间增加一条边(保证增加的边之前不存在),然后问点 a 和 b 之间是否存在一条路径长度为 k 的路径。每次询问相互独立,即每次增加的边在下一次的询问中不存在。
数据范围:
3≤n≤105,1≤q≤105,1≤x,y,a,b≤n,x=y,1≤k≤109
思路:
首先,在没有加边之前,假设点 a 和 b 最短路径为 len1,那么,如果 k≥len1,并且 k 和 len1 的奇偶性相同,那么就可以通过重复走某些边来达到路径长度为 k。
加边后,我们考虑使用加的边是否可以使得点 a 和 b间的最短路径变短或者可以有一条路径长度与之前最短路径奇偶性不同的 “最短路径”(奇偶性不同情况下的最短)。
把两种情况全部验证即可。
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+5;
const int mak=20;
const int inf=0x3f3f3f3f;
vector<int>pic[N];
int depth[N],fa[mak][N];
void dfs(int v,int p,int d)
{
depth[v]=d;
fa[0][v]=p;
for(int i=0;i<pic[v].size();i++)
{
int u=pic[v][i];
if(u!=p)
dfs(u,v,d+1);
}
}
void init(int n)
{
dfs(1,-1,0);
for(int k=0;k+1<mak;k++)
{
for(int i=1;i<=n;i++)
{
if(fa[k][i]==-1)
fa[k+1][i]=-1;
else
fa[k+1][i]=fa[k][fa[k][i]];
}
}
}
int lca(int u,int v)
{
if(depth[u]>depth[v])
swap(u,v);
for(int k=0;k<mak;k++)
{
if((depth[v]-depth[u])>>k&1)
v=fa[k][v];
}
if(u==v)
return u;
for(int k=mak-1;k>=0;k--)
{
if(fa[k][u]!=fa[k][v])
{
u=fa[k][u];
v=fa[k][v];
}
}
return fa[0][u];
}
int solve(int u,int v)
{
int t=lca(u,v);
return depth[u]+depth[v]-2*depth[t];
}
int main()
{
int n,q,u,v,x,y,k;
scanf("%d",&n);
for(int i=1;i<n;i++)
{
scanf("%d%d",&u,&v);
pic[u].push_back(v);
pic[v].push_back(u);
}
init(n);
scanf("%d",&q);
while(q--)
{
scanf("%d%d%d%d%d",&x,&y,&u,&v,&k);
int ans=inf;//k=1e9,初值赋大点
int tmp=solve(u,v);//原始树上两点间的最短距离
int res=min(solve(u,x)+solve(v,y),solve(u,y)+solve(v,x))+1;//使用增加的边的最短距离
//ans=min(ans,res);不能直接用二者中的最小值比较
if(tmp%2==k%2)
ans=tmp;
if(res%2==k%2)
ans=min(ans,res);
printf("%s\n",ans<=k?"YES":"NO");
}
return 0;
}