【Lca 倍增】codeforces 832D Misha, Grisha and Underground

CN_swords發表於2017-07-27

Link:http://codeforces.com/problemset/problem/832/D

/*
codeforces 832D
題意:每天 從a b c 三點選擇s f t,使s到f和t到f 經過的公共點個數最多。求每天的經過公共點最多個數。
題解:其實就是求a b c都能到的交叉路口,到abc最長的距離。abc的交叉路口可以是lca(a,b),lca(b,c),lca(a,c)
但是最長的距離肯定是在深度最深的lca。
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;

const int N = 1e5+5;
vector<int> ma[N];
int pa[N][20],d[N];

const int POW = 18; //2^17 > 100000
void dfs(int u,int fa)
{
    d[u] = d[fa]+1;
//    printf("%d %d\n",u,d[u]);
    pa[u][0] = fa;
    for(int i = 1; i < POW; i++)
        pa[u][i] = pa[pa[u][i-1]][i-1];
    for(int i = 0; i < ma[u].size(); i++)
    {
        int v = ma[u][i];
        if(v==fa) continue;
        dfs(v,u);
    }
}
int lca(int a, int b)
{
    if(d[a] > d[b]){
        int temp = a;
        a = b;
        b = temp;
        //a ^= b, b ^= a, a ^= b;
    }
    if(d[a] < d[b]){
        int del = d[b] - d[a];
        for(int i = 0; i < POW; i++)
            if(del&(1<<i))
                b = pa[b][i];
    }
    if(a != b)
    {
        for(int i = POW-1; i >= 0; i--)
            if(pa[a][i] != pa[b][i])
                a = pa[a][i] , b = pa[b][i];
        a = pa[a][0];
    }
    return a;
}

int dist(int a,int b){
    return d[a]+d[b]-2*(d[lca(a,b)]);
}


int main()
{
    memset(d,0,sizeof(d));
    int n,q;
    scanf("%d%d",&n,&q);
    for(int i = 2; i <= n; i++)
    {
        int a;
        scanf("%d",&a);
        ma[i].push_back(a);
        ma[a].push_back(i);
    }
    dfs(1,0);
    for(int i = 0; i < q; i++)
    {
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        int ans1 = lca(a,b);
        int ans2 = lca(b,c);
        int ans3 = lca(a,c);
        if(d[ans1] < d[ans2])   ans1 = ans2;
        if(d[ans1] < d[ans3])   ans1 = ans3;
        printf("%d\n",max(dist(ans1,a),max(dist(ans1,b),dist(ans1,c)))+1);
    }
    return 0;
}



相關文章