P3398 仓鼠找sugar(LCA)
Posted CCSU_Cola
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P3398 仓鼠找sugar(LCA)相关的知识,希望对你有一定的参考价值。
小仓鼠的和他的基(mei)友(zi)sugar住在地下洞穴中,每个节点的编号为1~n。地下洞穴是一个树形结构。这一天小仓鼠打算从从他的卧室(a)到餐厅(b),而他的基友同时要从他的卧室(c)到图书馆(d)。他们都会走最短路径。现在小仓鼠希望知道,有没有可能在某个地方,可以碰到他的基友?
小仓鼠那么弱,还要天天被zzq大爷虐,请你快来救救他吧!
题意:给出一棵树,给四个点a,b,c,d,问a-b的路径和c-d的路径有没有重合。
思路:若两个路径有重合那么一定有LCA(a,b)在c-d路径上或者LCA(c,d)在a-b路径上,先求出LCA(a,b)和LCA(c,d),然后如何判断一条路径上是否有这个点?若一个点a在x-y的路径上,那么一定有depth[a]>=depth[LCA(x,y)]且有LCA(a,x)==a或者LCA(a,y)==a,(depth表示该点在树上的深度)。
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1000100;
struct node{
int x,to;
};
node e[maxn<<1];
int n,m,idx,h[maxn];
void add(int a,int b){
e[idx].x=b,e[idx].to=h[a],h[a]=idx++;
}
int depth[maxn],fa[maxn][22];
void bfs(int root){
memset(depth,0x3f,sizeof depth);
depth[0]=0,depth[root]=1;
queue<int>q;
q.push(root);
while(!q.empty()){
int t=q.front();
q.pop();
for(int i=h[t];i!=-1;i=e[i].to){
int j=e[i].x;
if(depth[j]>depth[t]+1){
depth[j]=depth[t]+1;
q.push(j);
fa[j][0]=t;
for(int i=1;i<=20;i++){
fa[j][i]=fa[fa[j][i-1]][i-1];
}
}
}
}
}
int LCA(int a,int b){
if(depth[a]<depth[b]){
swap(a,b);
}
for(int k=20;k>=0;k--){
if(depth[fa[a][k]]>=depth[b]){
a=fa[a][k];
}
}
if(a==b)return a;
for(int k=20;k>=0;k--){
if(fa[a][k]!=fa[b][k]){
a=fa[a][k];
b=fa[b][k];
}
}
return fa[a][0];
}
int main(){
memset(h,-1,sizeof h);
scanf("%d%d",&n,&m);
int a,b;
for(int i=1;i<n;i++){
scanf("%d%d",&a,&b);
add(a,b),add(b,a);
}
bfs(1);
int c,d;
for(int i=1;i<=m;i++){
scanf("%d%d%d%d",&a,&b,&c,&d);
int aa=LCA(a,b);
int bb=LCA(c,d);
//cout<<a<<" "<<b<<" "<<c<<" "<<d<<endl;
//cout<<aa<<" "<<bb<<endl;
if(depth[aa]>=depth[bb]){
if(LCA(aa,c)==aa||LCA(aa,d)==aa){
printf("Y\\n");
}
else printf("N\\n");
}
else{
if(LCA(bb,a)==bb||LCA(bb,b)==bb){
printf("Y\\n");
}
else printf("N\\n");
}
}
}
也可以采用树链剖分,将a-b路径的点置为1,然后查询c-d路径的最大值,如果大于0则说明有重合,否则没有重合,每次查询之后要将之前置为1的区间再置为0即可。
以上是关于P3398 仓鼠找sugar(LCA)的主要内容,如果未能解决你的问题,请参考以下文章