题目链接
题目大意:给你一棵树,然后还有q个询问,每次询问给出四个点,前两个点组成一条路径,后两个点一条,问这两条路径是否相交。
第一反应是树链剖分暴力修改和查询,事实证明这种做法也可以过,就是时间消耗大点,1s时限最后最大的测试点用了500多ms,数据大点估计就挂了。
但是我点开这个题标签一看,居然没有树剖,仔细一想,确实是,如果两条路径有交的话,那么其中两点的lca必然在另一条路径上,那么问题就变成了判断点是否在路径上。
那么假设要判断x是否在路径ab上,很简单,如果满足这个式子才有可能在路径ab上:dis(a,b)==dis(x,a)+dis(x,b)
那么问题就结束了,下面是代码。因为之前刚打了个树剖,所以lca就用树剖来求了,可以改成倍增。
#include<cstdio>
#include<vector>
#include<cstring>
#include<queue>
#include<cmath>
#include<string>
#include<fstream>
#include<map>
#include<algorithm>
#include<iostream>
#define lk (k<<1)
#define rk (k<<1|1)
using namespace std;
typedef long long ll;
const double eps=1e-8;
const double pi=acos(-1.0);
const int inf=0x3f3f3f3f;
const int N=1e5+10;
struct edge
{
int v,next;
}e[N<<1];
int head[N],cnt;
void add(int u,int v)
{
e[++cnt].v=v;
e[cnt].next=head[u];
head[u]=cnt;
}
int n,m;
int dep[N],fa[N],son[N];
int size[N],top[N];
void init()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n-1;i++){
int u,v;
scanf("%d%d",&u,&v);
add(u,v);
add(v,u);
}
}
void dfs1(int u,int f)
{
dep[u]=dep[f]+1;
size[u]=1;
fa[u]=f;
for(int i=head[u];i;i=e[i].next)
{
int v=e[i].v;
if(v==f) continue;
dfs1(v,u);
size[u]+=size[v];
if(size[v]>size[son[u]]) son[u]=v;
}
}
void dfs2(int u,int f)
{
if(son[u]){
top[son[u]]=top[u];
dfs2(son[u],u);
}
for(int i=head[u];i;i=e[i].next)
{
int v=e[i].v;
if(v==f) continue;
if(!top[v]){
top[v]=v;
dfs2(v,u);
}
}
}
int lca(int u,int v)
{
int fu=top[u],fv=top[v];
while(fu!=fv){
if(dep[fu]<dep[fv]) swap(fu,fv),swap(u,v);
u=fa[fu];
fu=top[u];
}
if(dep[u]>dep[v]) swap(u,v);
return u;
}
int dis(int x,int y)
{
return dep[x]+dep[y]-2*dep[lca(x,y)];
}
void solve()
{
while(m--){
int a,b,c,d;
scanf("%d%d%d%d",&a,&b,&c,&d);
int x=lca(a,b);
int y=lca(c,d);
if(dis(a,b)==dis(y,a)+dis(y,b)){
puts("Y");
}else if(dis(c,d)==dis(x,c)+dis(x,d)){
puts("Y");
}else puts("N");
}
}
int main()
{
init();
dfs1(1,0);
fa[1]=1;
top[1]=1;
dfs2(1,0);
solve();
return 0;
}