codeforces 832 D Misha, Grisha and Underground(倍增)

来源:互联网 发布:好搜小说软件 编辑:程序博客网 时间:2024/06/15 02:37

题意:

给出n个点的树,q个询问,每次询问给出3个数x,y,z问选两个点作为起点,到第三个点的路径有多少个点重合。


解题 思路:

distance(x,y)=distance(x,root)+distance(y, root)-2*distance(lca(x,y),root).

x,y为起点时,答案就是(dis(x,z)+dis(y,z)-dis(x,y))/2 。


重点讲一下倍增吧,第一次写,求LCA简直不能更简单。


倍增顾明思义就是成倍增大。两个点往上找lca的时候就是一个每次往上的距离成倍变大的一个过程。


倍增求LCA需要预处理祖先数组。fa[i][j],表示第i个点的第2^j个祖先,j从0开始。

这个处理起始非常简单,fa[i][j]=fa[fa[i][j-1][i].因为第2^j个祖先等价于当前点第2^(j-1)个祖先的第2^(j-1)个祖先,一次跑一遍就可以了。


另外还需要处理处每个点的深度,这个简单,必要的话要处理下到根节点的距离,没有边权的时候其实就是深度。


求a和b的lca的时候,先让深度大的节点先往上跳,跳的过程可以用二进制优化,设深度差为dif,把dif二进制拆分,只需要对为1的对应位j跳到第2^j个祖先就可以。


当深度一样时,判断下当前是否a和b是否相等,不相等让a和b同时往上跳,j从高到低判断下第2^j个祖先是否相同,相同就不往上跳,知道碰到祖先不同再往上跳。循环结束后,a和b的父亲就是lca了。


代码:

#include <bits/stdc++.h>#define ps push_backusing namespace std;const int maxn=1e5+5;int dp[maxn][22];int dep[maxn];vector<int>edg[maxn];void dfs(int x, int fa){    dep[x]=dep[fa]+1;    int v;    if(fa)    for(int i=1; i<20; i++)    {        dp[x][i]=dp[dp[x][i-1]][i-1];       }    for(int i=0; i<(int)edg[x].size(); i++)    {        v=edg[x][i];        if(v==fa)continue;        dp[v][0]=x;        dfs(v, x);    }    return;}int dis(int a, int b){    int x=a, y=b;    if(dep[a]<dep[b])    {        swap(a,b);    }    int i, dif=dep[a]-dep[b];    for(i=0; (1<<i)<=dif; i++)    {        if(dif&(1<<i))        {            a=dp[a][i];        }           }    if(a==b)    {        return dep[x]+dep[y]-2*dep[a];    }    for(i=20; i>=0 && a!=b; i--)    {        if(dp[a][i]==dp[b][i])continue;        a=dp[a][i]; b=dp[b][i];    }    a=dp[a][0];    return dep[x]+dep[y]-2*dep[a];}int query(int x, int y, int z){//    printf("%d %d %d\n", dis(x, z), dis(y, z), dis(x, y));    return (dis(x, z)+dis(y, z)-dis(x, y))/2;}int main(){    int n, x, q, i, j;       cin>>n>>q;    for(i=2; i<=n; i++)    {        scanf("%d", &x);        edg[x].ps(i);    }    dp[1][0]=1;    dep[1]=1;    dfs(1, 0);//    for(i=1; i<=3; i++)printf("%d\n", dp[i][0]);    int y, z;    for(i=1; i<=q; i++)    {        scanf("%d%d%d", &x, &y, &z);        printf("%d\n", max(max(query(x, y, z), query(x, z, y)), query(z, y, x))+1);    }}




阅读全文
0 0
原创粉丝点击