LCA转换为RMQ

对有根树T进行DFS,将遍历到的结点按照顺序记下,我们将得到一个长度为2N – 1的序列,称之为T的欧拉序列F

每个结点都在欧拉序列中出现,我们记录结点u在欧拉序列中第一次出现的位置为we(u)

例如下面图片中的一棵树:

无标题

欧拉序列(QQ):1  2  5  2  6  2  1  3  1  4  1

深度序列(DP): 1  2  3  2  3  2  1  2  1  2  1

根据DFS的性质,对于两结点u、v,从pos(u)遍历到pos(v)的过程中经过LCA(u, v)有且仅有一次,且深度是深度序列B[we(u)…we(v)]中最小的

至此,LCA问题已经转换成了RMQ问题.

下面给出C++代码:

 

#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
struct
{
      int x,y,n;
}e[200001];
int ff[200001];
int o,dp[200001],q[200001][20],f[200001][20];
bool v[200001];
int qq[200001],we[200001],time;
void add(int a,int b)
{
     e[++o].x=a;
     e[o].y=b;
     e[o].n=ff[a];
     ff[a]=o;
}
float log2(int n)
{
    return log10(n)/log10(2);
}
void dfs(int x,int deep)
{
     v[x]=true;
     qq[++time]=x;
     we[x]=time;
     dp[time]=deep;
     int t=ff[x];
     while  (e[t].y!=0)
     {
            dfs(e[t].y,deep+1);
            qq[++time]=x;
            dp[time]=deep;
            t=e[t].n;
     }
}
void find(int a,int b)
{
     int minn=(1<<30);
     int dk=(int)log2(b-a+1);
     if (f[a][dk]<f[b-(1<<dk)+1][dk]) minn=q[a][dk];
     else minn=q[b-(1<<dk)+1][dk];
     printf("%d\n",minn);
}

int main()
{
    freopen("turn.in","r",stdin);
    freopen("turn.out","w",stdout);
    int n;scanf("%d",&n);
    o=0;
    time=0;
    for (int i=1;i<n;i++)
    {
        int a,b;scanf("%d%d",&a,&b);
        add(a,b);v[b]=true;
    }
    int biao;
    for (int i=1;i<=n;i++)
      if (v[i]==false) {biao=i;break;}
    dfs(biao,1);
    for (int i=1;i<=time;i++)
      f[i][0]=dp[i];
    for (int i=1;i<=time;i++)
      q[i][0]=qq[i];
    for (int j=1;j<=(int)log2(time);j++)
      for (int i=1;i<=time;i++)
      {
          if (i+(1<<j)-1>time) break;
          if (f[i][j-1]<f[i+(1<<(j-1))][j-1])
          {
                                             f[i][j]=f[i][j-1];
                                             q[i][j]=q[i][j-1];
          }else
          {
               f[i][j]=f[i+(1<<(j-1))][j-1];
               q[i][j]=q[i+(1<<(j-1))][j-1];
          }
      } 
        int a,b;
        scanf("%d%d",&a,&b);
        if (we[a]>we[b])
         find(we[b],we[a]);
         else find(we[a],we[b]);
    return 0;
}

 

文章出处:http://www.cnblogs.com/foreverzsz/archive/2010/11/07/1871206.html

(foreverzsz原创,转载请注明出处)

转载于:https://www.cnblogs.com/foreverzsz/archive/2010/11/07/1871206.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值