天天看點

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*/