题意:有一个n个节点的数,进行q次询问,每次询问包含x,y,a,b,k,用一条边连接x,y节点,问是否存在从a到b的一条路径,使得路径长度恰好为k,一条路径可以经过重复的边和重复的点
思路:假设有一条路径,其长度小于k,点在移动的过程中可以左右横跳来增加长度,这种操作每次会增加2,因此当长度x和k的奇偶性相同时,(k-x)%2==0,那么该路径成立,否则不成立
另外,如果该路线长度大于k,必定不成立
很显然,这道题如果考虑a到b的线路的话,有三条路可以走
(1)a直接沿着树到b
(2)a到x,再从x到y,再从y到b
(3)a到y,再从y到x,再从x到b
a从其中一条线路到b再从另外两条线路回到a,有可能改变路径长度的奇偶性,无需考虑从相同路径回来。
比如a从(1)到b,如果考虑从(1)回来,和左右横跳没有区别,这个圈绝对是偶数,而从其他线路回来,这个圈的长度有可能变成奇数,再从a经过三条路径到达b,和直接a经过三条路径到达b,总路线的奇偶性不同,有可能就成立了
考虑重链剖分,将树分割成若干条重链,因此每条重链的时间戳是连续的,当计算节点a到节点b的路径长度时:
(1)当ab不在同一条链上,两点中所处链的顶端深度较深的往上跳,跳到该顶端的父节点上,计算路径
(2)不断重复(1),直到ab处于同一条链上
(3)同一条链的时间戳是连续的,因此可以根据时间戳计算路径
代码实现
#include<bits/stdc++.h>
#define ll long long
#define IOS ios::sync_with_stdio(false);cin.tie(0)
using namespace std;
const int maxn=1e5+20;
vector<int>vv[maxn];
inline void add(int u,int v)
{
vv[u].push_back(v);
vv[v].push_back(u);
}
int ans1=0,ans2=0,ans3=0;
int fa[maxn],son[maxn],siz[maxn],dfn[maxn],dep[maxn],top[maxn];
//记录节点的父亲节点,重子节点,子树大小,时间戳,深度,链的顶端
int cnt,k;
void dfs1(int u,int fat)//寻找重子节点,父节点,子树大小,深度
{
siz[u]=1;
fa[u]=fat;
dep[u]=dep[fat]+1;
for(int to:vv[u])
{
if(to==fat)
continue;
dfs1(to,u);
siz[u]+=siz[to];
if(siz[to]>siz[son[u]])//寻找重子节点
son[u]=to;
}
}
void dfs2(int u,int t)//获取时间戳,标记当前节点所处的链的顶端
{
dfn[u]=++cnt;//时间戳
top[u]=t;
if(!son[u])
return;
dfs2(son[u],t);//重子节点所处链的顶端和当前节点u相同
for(int to:vv[u])
{
if(to==fa[u]||to==son[u])
continue;
dfs2(to,to);
}
}
inline bool judge(const int &x)//判断当前路径是否成立
{
//当此路径奇偶性和k相同并且比k小
return k>=x&&k%2==x%2;
}
inline bool merge(const int&x)//两个线路组合绕一圈再从起点a走向终点
{
return judge(ans1+ans2+x)||judge(ans2+ans3+x)||judge(ans3+ans1+x);
}
int query(int x,int y)
{
int sum=0;
while(top[x]!=top[y])//当两点不在同一条链上时
{
if(dep[top[x]]<dep[top[y]])//保证x为所处链的顶端较深的
swap(x,y);
sum+=dfn[x]-dfn[top[x]]+1;//从x跳到x所处的链的顶端的父节点上
x=fa[top[x]];
}
if(dep[x]<dep[y])//保证x为深度大的
swap(x,y);
sum+=dfn[x]-dfn[y];
return sum;
}
int main(){
IOS;
int n;
cin>>n;
for(int i=1;i<n;i++)
{
int u,v;
cin>>u>>v;
add(u,v);
}
int q;
dfs1(1,0);
dfs2(1,1);
cin>>q;
while(q--)
{
int x,y,a,b;
cin>>x>>y>>a>>b>>k;
ans1=query(a,b);//沿着原本的路从a到b
ans2=query(a,x)+query(b,y)+1;//a到x,b到y,然后经过连结xy的路径
ans3=query(a,y)+query(b,x)+1;//b到x,a到y,然后经过连结xy的路径
if(ans1>k&&ans2>k&&ans3>k)//如果三种路径都长了
{
cout<<"NO\n";
continue;
}
bool f=0;
if(judge(ans1)||judge(ans2)||judge(ans3))
f=1;
if(merge(ans1)||merge(ans2)||merge(ans3))
f=1;
if(f)
cout<<"YES\n";
else
cout<<"NO\n";
}
return 0;
}