[BZOJ1787][AHOI2008]Meet 紧急集合(LCA)

题目描述

传送门

题解

两两求lca,如果有两个相同,另外一个就是要到达的点。
求lca很好理解,因为这个点一定要在这些点两两之间的唯一路径上,如果去别的点的话就太蠢了。
如果两对lca相同,那么说明有两个点到这个lca的路需要走重复的一段,那么我们就不要这么蠢,让另外一个点自己走这段路就好了。

代码

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;

const int max_n=5e5+5;
const int max_e=max_n*2;
const int sz=19;

int n,m,x,y,z,other,goal,ans;
int tot,point[max_n],next[max_e],v[max_e];
int f[max_n][sz],s[max_n][sz];
int h[max_n],mi[sz];
struct hp{
    int r,sum;
};
hp r1,r2,r3;

inline void addedge(int x,int y){
    ++tot; next[tot]=point[x]; point[x]=tot; v[tot]=y;
    ++tot; next[tot]=point[y]; point[y]=tot; v[tot]=x;
} 

inline void dfs(int x,int fa,int dep){
    h[x]=dep;
    for (int i=1;i<sz;++i){
        if (h[x]-mi[i]<1) break;
        f[x][i]=f[f[x][i-1]][i-1];
        s[x][i]=s[x][i-1]+s[f[x][i-1]][i-1];
    }
    for (int i=point[x];i;i=next[i])
      if (v[i]!=fa){
        f[v[i]][0]=x;
        s[v[i]][0]=1;
        dfs(v[i],x,dep+1);
      }
}

inline hp lca(int x,int y){
    hp ans;
    int sum=0;
    if (h[x]<h[y]) swap(x,y);
    int k=h[x]-h[y];
    for (int i=0;i<sz;++i)
      if ((k>>i)&1)
       sum+=s[x][i],x=f[x][i];
    if (x==y) return ans=(hp){x,sum};
    for (int i=sz-1;i>=0;--i)
      if (f[x][i]!=f[y][i]){
        sum+=s[x][i]+s[y][i];
        x=f[x][i],y=f[y][i];
      } 
    sum+=s[x][0]+s[y][0];
    return ans=(hp){f[x][0],sum};
}

int main(){
    scanf("%d%d",&n,&m);
    mi[0]=1;
    for (int i=1;i<sz;++i) mi[i]=mi[i-1]*2;
    for (int i=1;i<n;++i){
        scanf("%d%d",&x,&y);
        addedge(x,y);
    }
    dfs(1,0,1);
    for (int i=1;i<=m;++i){
        scanf("%d%d%d",&x,&y,&z);
        r1=lca(x,y);
        r2=lca(y,z);
        r3=lca(x,z);
        if (r1.r==r2.r) other=y,goal=r3.r,ans=r3.sum;
        else if (r2.r==r3.r) other=z,goal=r1.r,ans=r1.sum;
        else if (r1.r==r3.r) other=x,goal=r2.r,ans=r2.sum;
        ans+=lca(other,goal).sum;
        printf("%d %d\n",goal,ans);
    }
}

总结

①看清楚数据范围!别再RE了!
②倍增维护一个值时,lca先更新答案再蹦。

你可能感兴趣的:(LCA,bzoj,AHOI)