天天看点

hdoj 2586 <LCA转RMQ算法--学习之路>

题目链接: ​​hdoj 2586​​

思路:

先找一点作为起点通过DFS回溯记录每次DFS的点数(就是从根节点遍历所有点的去路和回来时的路)

用vis [  ] 数组记录每次走过的是哪一个点--

用D [ ] 来对应vis [ ] 数组上的点的深度(与根节点的距离)

用R [ ] 来记录每个点第一次出现的位置(两点的最初位置之间(含他们自身)的点中深度最小的点就是他们的最近祖先)

然后查找RMQ算法就可以查到最近祖先了---

样例:

hdoj 2586 <LCA转RMQ算法--学习之路>

我这个程序是让n当根节点--(因为这一题让1当根节点,,代码错的也会过--不便于学习新知识--(这题数据太弱))

看倒数第二行:回溯的顺序就是13-10-12-10-7------

最后一行就是回溯的每个位置的点的深度(用RMQ时,就是根据这个比较的)

倒数第四行是每个点第一次出现的位置--

比如求3和4的最近祖先时:

3第一次出现在8的位置,4是18的位置--然后我们在最后一行8-18中找最小的深度()

找到最小深度4--再看倒数第二行发现对应的是3----就是说明点3是3和4的最近祖先

回溯过程图:

hdoj 2586 <LCA转RMQ算法--学习之路>

代码上的中间值便于理解这个方法,所以就没有删除--

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
#define MA 420000
int n,m;
struct node{
  int to,w,next;
}edge[MA*2];
int head[MA];
int D[MA];//深度 
int R[MA];//每个点第一次出现的位置 
int QM[MA][33];
int vis[MA*2],kp;//vis记录第i个位置的数 
int dis[MA];
void bfs(int now,int fa,int chang)
{
  dis[now]=chang;
  for (int j=head[now];j!=-1;j=edge[j].next)
  {
    if (edge[j].to==fa) continue;
    bfs(edge[j].to,now,chang+edge[j].w);
  }
}
void DFS(int now,int fa,int deep)
{
  D[kp]=deep;
  R[now]=kp;
  vis[kp++]=now;
  for (int j=head[now];j!=-1;j=edge[j].next)
  {
    if (edge[j].to==fa) continue;
    DFS(edge[j].to,now,deep+1);
    D[kp]=deep;
    vis[kp++]=now;
  } 
}
int main()
{
  int t;scanf("%d",&t);
  while (t--)
  {
    scanf("%d%d",&n,&m);
    int a,b,c;
    for (int i=1;i<=n;i++)
    head[i]=-1;
    for (int i=0;i<n-1;i++)
    {
      scanf("%d%d%d",&a,&b,&c);
      edge[i*2].to=b;edge[i*2].w=c;
      edge[i*2].next=head[a];
      head[a]=i*2;
      edge[i*2+1].to=a;edge[i*2+1].w=c;
      edge[i*2+1].next=head[b];
      head[b]=i*2+1;
    }
    kp=1;
    bfs(n,-1,0);
    DFS(n,-1,1);
  /*  for (int i=1;i<=n;i++)
    printf("%4d",i);printf("\n");
    for (int i=1;i<=n;i++)
    printf("%4d",R[i]);printf("\n");
    for (int i=1;i<kp;i++)
    printf("%4d",i);printf("\n");*/
    for (int i=1;i<kp;i++)
    {
  //    printf("%4d",vis[i]);
      QM[i][0]=i;
    }
  /*  printf("\n");
    for (int i=1;i<kp;i++)
      printf("%4d",D[i]);
      printf("\n");*/
    int nn=kp-1;
    int p=log2(nn);
    for (int i=1;i<=p;i++)
    for (int j=1;j+(1<<i)-1<=nn;j++)
    {
      int aa=QM[j][i-1];
      int bb=QM[j+(1<<(i-1))][i-1];
  //    printf("%d   %d   %d   %d 66666666666\n",aa,bb,D[aa],D[bb]);
      if (D[aa]<=D[bb])
        QM[j][i]=aa;
      else
        QM[j][i]=bb;
  //    printf("%d  %d  %d\n",j,i,QM[j][i]); 
    }
  /*  printf("\n");
    for (int i=1;i<=n;i++)
    printf("%4d\n",dis[i]);
  */  while (m--)
    {
      scanf("%d%d",&a,&b);
      int aa=min(R[a],R[b]);
      int bb=max(R[a],R[b]);
  //    printf("%d   %d     9999999999999\n",aa,bb);
      int pp=log2(bb+1-aa);
      int aaa=QM[aa][pp];
      int bbb=QM[bb+1-(1<<pp)][pp];
     //     c=min(QM[aa][pp],QM[bb+1-(1<<pp)][pp]); 
  //      printf("%d   %d    666\n",aaa,bbb);
        if (D[aaa]<=D[bbb])
        c=aaa;
        else
        c=bbb;
  //      printf("%d\n",c);
        c=vis[c]; 
      //    printf("ans    :   %d\n",dis[a]+dis[b]-2*dis[c]);
      printf("%d\n",dis[a]+dis[b]-2*dis[c]);
    }
  }
  return 0;
}
/*2
13 13
1 2  1
1 3  1
1 4  1
3 5  1
3 6  1
3 7  1
6 8  1
6 9  1
7 10  1
7 11  1
10 12  1
10 13  1*/