仓鼠找sugar
题目描述
小仓鼠的和他的基(mei)友(zi)sugar住在地下洞穴中,每个节点的编号为1~n。地下洞穴是一个树形结构。这一天小仓鼠打算从从他的卧室(a)到餐厅(b),而他的基友同时要从他的卧室(c)到图书馆(d)。他们都会走最短路径。现在小仓鼠希望知道,有没有可能在某个地方,可以碰到他的基友?
小仓鼠那么弱,还要天天被zzq大爷虐,请你快来救救他吧!
输入格式
第一行两个正整数n和q,表示这棵树节点的个数和询问的个数。
接下来n-1行,每行两个正整数u和v,表示节点u到节点v之间有一条边。
接下来q行,每行四个正整数a、b、c和d,表示节点编号,也就是一次询问,其意义如上。
输出格式
对于每个询问,如果有公共点,输出大写字母“Y”;否则输出“N”。
这道题最重要的推论就是:当两个点 a,b 的lca在另外两个点 c,d 的连线上时,a,b线段 和 c,d线段 相交;
知道这个还要知道一个:怎么判断一个点是否在由两个点相连的线段上?这跟判断一个点是否在 i到j 的最短路上是一样的;
代码:
#include<bits/stdc++.h>
#define LL long long
#define pa pair<int,int>
#define ls k<<1
#define rs k<<1|1
#define inf 0x3f3f3f3f
using namespace std;
const int N=200100;
const int M=1000100;
const LL mod=100000000;
int n,q,head[N],cnt,lg[N],dep[N],fa[N][40],in[N];
struct Node{
int to,nex;
}edge[M];
void add(int p,int q){
edge[cnt].to=q;
edge[cnt].nex=head[p];
head[p]=cnt++;
}
void dfs(int sn,int ft){
dep[sn]=dep[ft]+1,fa[sn][0]=ft;
for(int i=1;i<=lg[dep[sn]];i++) fa[sn][i]=fa[fa[sn][i-1]][i-1];
for(int i=head[sn];~i;i=edge[i].nex){
if(edge[i].to!=ft) dfs(edge[i].to,sn);
}
}
int lca(int x,int y){
if(dep[x]<dep[y]) swap(x,y);
while(dep[x]>dep[y]) x=fa[x][lg[dep[x]-dep[y]]-1];
if(x==y) return x;
for(int k=lg[dep[x]]-1;k>=0;k--){
if(fa[x][k]!=fa[y][k]){
x=fa[x][k],y=fa[y][k];
}
}
return fa[x][0];
}
int main(){
memset(head,-1,sizeof(head));
cin>>n>>q;
for(int i=1;i<n;i++){
int p,q;
scanf("%d%d",&p,&q);
in[q]++;
add(p,q),add(q,p);
}
for(int i=1;i<=n;i++) lg[i]=lg[i-1]+(1<<lg[i-1]==i);//预处理(log_2(i))+1的值
int st=1;
for(int i=1;i<=n;i++) if(in[q]==0) st=i;
dfs(st,0);
while(q--){
int a,b,c,d;
scanf("%d%d%d%d",&a,&b,&c,&d);
int e=lca(a,b);
int cd=dep[c]+dep[d]-2*dep[lca(c,d)];
int ce=dep[c]+dep[e]-2*dep[lca(c,e)];
int ed=dep[e]+dep[d]-2*dep[lca(e,d)];
if(ce+ed==cd) cout<<"Y"<<endl;
else{
int e=lca(c,d);
int ab=dep[a]+dep[b]-2*dep[lca(a,b)];
int ae=dep[a]+dep[e]-2*dep[lca(a,e)];
int eb=dep[e]+dep[b]-2*dep[lca(e,b)];
if(ae+eb==ab) cout<<"Y"<<endl;
else cout<<"N"<<endl;
}
}
return 0;
}