P4281 [AHOI2008]紧急集合 / 聚会

P4281 [AHOI2008]紧急集合 / 聚会

lca

题意:求3个点的lca,以及3个点与lca的距离之和。


 

性质:设点q1,q2,q3

两点之间的lca

t1=lca(q1,q2)

t2=lca(q1,q3)

t3=lca(q2,q3)

一定有两个lca重合


3个点的公共lca一定在 两个lca之间(有重合)

而重合的lca深度必定更浅

所以3个点的公共lca就是那个不重合的lca

(完整证明还是看题解吧TAT)

使用倍增法(懒得打树剖了qwq)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cctype>
using namespace std;
template <typename T> inline void read(T &x){
    char c=getchar(); x=0;
    while(!isdigit(c)) c=getchar();
    while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
}
template <typename T> inline void output(T x){
    if(!x) {putchar(48); return ;}
    int wt[50],l=0;
    while(x) wt[++l]=x%10,x/=10;
    while(l) putchar(wt[l--]+48);
}
int n,m,ans,fa[19][500002],d[500002];
int cnt,hd[500002],nxt[1000002],ed[500002],poi[1000002]; //注意边开2倍
inline void add(int x,int y){
    nxt[ed[x]]=++cnt; hd[x]= hd[x] ? hd[x]:cnt;
    ed[x]=cnt; poi[cnt]=y;
}
inline void dfs(int x,int _fa){
    d[x]=d[_fa]+1; fa[0][x]=_fa;
    for(int i=1;(1<<i)<=d[x];++i) fa[i][x]=fa[i-1][fa[i-1][x]];
    for(int i=hd[x];i;i=nxt[i]) if(poi[i]!=_fa) dfs(poi[i],x);
}
inline int lca(int x,int y){
    if(d[x]<d[y]) swap(x,y);
    for(int i=18;i>=0;--i) if(d[fa[i][x]]>=d[y]) x=fa[i][x];
    if(x==y) return x;
    for(int i=18;i>=0;--i) if(fa[i][x]!=fa[i][y]) x=fa[i][x],y=fa[i][y];
    return fa[0][x];
}
//----------lca模板---------
int main(){ read(n); read(m); int q1,q2,q3; for(int i=1;i<n;++i) read(q1),read(q2),add(q1,q2),add(q2,q1); dfs(1,0); for(int i=1;i<=m;++i){ read(q1),read(q2),read(q3); ans=0; int t1=lca(q1,q2); int t2=lca(q1,q3); int t3=lca(q2,q3); if(t1==t2) ans=t3; else if(t1==t3) ans=t2; else if(t2==t3) ans=t1; //找到不同的那个lca output(ans),putchar(' '); output(d[q1]+d[q2]+d[q3]-d[t1]-d[t2]-d[t3]),putchar('\n'); }return 0; }

 

posted @ 2018-09-25 13:51  kafuuchino  阅读(227)  评论(0编辑  收藏  举报