1. 程式人生 > >lca倍增演算法模板

lca倍增演算法模板

1431: LCA 最近公共祖先

時間限制: 1 Sec  記憶體限制: 128 MB
提交: 244  解決: 36
[提交][狀態]

題目描述

給一棵樹, 節點數為N(1 <= N <= 250,000), 和Q(0 <= Q <= 100,000)個詢問, 對於每個詢問求出所求兩點的最近公共祖先

輸入

第一行: 節點數N

以下N行, 第i + 1行: 點i的父親節點Father[i] (假定根的父親是0)

下一行為詢問數Q

每個詢問包含兩個整數i, j你的程式應按照詢問的次序回答出所求兩點的最近公共祖先

輸出

共N行, 第i行: 第i個詢問的答案

樣例輸入

12
0
1
1
1
2
2
3
3
8
8
9
10
5
5 12
12 7
9 12
10 12
12 12

樣例輸出

1
3
8
10
12
倍增演算法:主要分兩步。 第一步:將兩個節點"竄"到一個深度 第二步:兩個節點一起"竄"到他們的lca(最近公共祖先)。 '竄'的操作,如果你每個節點一下,一下,一下的竄,會非常慢。。所以就有了倍增,要一次竄2^n個節點。當然如果過頭了就少竄一點。如果你寫過01 揹包,它的思路和2進位制拆包很像。 看起來好像很簡單的樣子>-<程式碼如下(帶註釋)
#include<iostream>
#include<cmath>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<ctime>
using namespace std;
const int maxn=250020;
int n,q;
int g[100][100],ne,root;
int p[maxn][20];//p[i][j]表示i結點的第2^j祖先
int deep[maxn];//i的深度  
int use[1000][1000];
void build(int x)//處理樹深度 
{ 
    for(int i=1;i<=n;i++)
     if(g[x][i]==1)
      {
        deep[i]=deep[x]+1;
        p[i][0]=x;
				g[x][i]=g[i][x]=0;
				build(i);	
      }
}
 void pre()//處理祖先 
{
  int i,j; 
  for(j=1;(1<<j)<=n;j++)
    for(i=1;i<=n;i++)//! !j在i前面!!記住 
      if(p[i][j-1]!=-1)
        p[i][j]=p[p[i][j-1]][j-1];//i的第2^j祖先就是i的第2^(j-1)祖先的第2^(j-1)祖先
}
int lca(int a,int b)//最近公共祖先
{
//使a,b兩點的深度相同
  int i,j;
  if(deep[a]<deep[b])swap(a,b);
  for(i=0;(1<<i)<=deep[a];i++);
  i--;
  
  for(j=i;j>=0;j--)
    if(deep[a]-(1<<j)>=deep[b])
      a=p[a][j];
  if(a==b)return a;
  
//倍增法,每次向上進深度2^j,找到最近公共祖先的子結點
  for(j=i;j>=0;j--)
  {
    if(p[a][j]!=-1&&p[a][j]!=p[b][j])//!!
    {
      a=p[a][j];
      b=p[b][j];
    }
  }
  return p[a][0];//即正好竄到兩個子樹上,最近祖先為它的j=0的祖先 
}
int main()
{
  cin>>n>>q;
  for(int i=1;i<=n;i++)
   {
   	int a,b;
   	 cin>>a>>b;
   	 g[a][b]=g[b][a]=1;
   }
   p[1][0]=-1;
   deep[1]=1;
   build(1);
   pre();
  for(int i=1;i<=q;i++)
   {
      int x,y;
      scanf("%d%d",&x,&y);
      printf("%d\n",lca(x,y));
   }

return 0;
}

相關推薦

lca倍增演算法模板

1431: LCA 最近公共祖先 時間限制: 1 Sec  記憶體限制: 128 MB提交: 244  解決: 36 [提交][狀態] 題目描述 給一棵樹, 節點數為N(1 <= N &

lca倍增演算法學習記錄

找最近公共父節點這問題很容易想到讓兩節點一起往上走最後相遇,但這樣的dfs顯然很慢,於是就需要倍增。就是用二進位制的思維,以1,2,4,8等2的階層步長接近答案,比一步一步向上要快很多。 所以要dfs出來點的2^k的父親節點與該節點的深度。 找lca時先將下

LCA RMQ演算法模板

題目:HDU 3078 Network 主要貼一下LCA RMQ的演算法模板,供以後回憶 #include <iostream> #include <cstdio> #include <cstdlib> #include <alg

lca倍增演算法

HDU2586 要注意只有簡單邊相連的圖是一棵樹,樹有n-1條邊 倍增之前要先存雙向邊,以任意頂點為根(比如1)dfs遍歷確定樹。 倍增前求f[i,j]時先賦值為-1便於判斷f[ij-1]祖先存不存在 倍增時先將兩個點調至同一高度(注意最後的微調每次只上升一個高度) pro

POJ-1330-Nearest Common Ancestors(LCA+倍增模板題)

題目連結:http://poj.org/problem?id=1330 Description A rooted tree is a well-known data structure in computer science and engineering. An example is sh

LCA-Tarjan,RMQ,倍增演算法超詳細原理講解+python實踐(Lowest Common Ancestor of a Binary Tree)

最近公共祖先演算法: 通常解決這類問題有兩種方法:線上演算法和離線演算法 線上演算法:每次讀入一個查詢,處理這個查詢,給出答案 離線演算法:一次性讀入所有查詢,統一進行處理,給出所有答案 我們接下來介紹一種離線演算法:Tarjan,兩種線上演算法:RMQ,倍增演算法 Tarjan

最近公共祖先(LCA) 【倍增演算法

題目連結:洛谷-P3379 ## **題目描述**: 如題,給定一棵有根多叉樹,請求出指定兩個點直接最近的公共祖先。 思路 這是一個裸的LCA問題,即求書上兩個節點的最近公共祖先。 我們可以用樹上倍增來做; 當然,在做之前我們假設不知道該演算法。那麼我們如何來做

DP之倍增演算法解決LCA問題

LCA,最近公共祖先,也就是樹上兩個節點的相同的祖先裡距離它們最近的(也就是深度最深的)。倍增演算法用於解決LCA問題的線上查詢。 比如要找x和y的LCA,一般會怎麼做? 首先是暴力。一種是DFS遍歷說有點,無需預處理,查詢是O(n)的。還有一種暴力是先一步一步將x和y提到同一高度,然後

[HDU2586] How far away ?{LCA.tarjan演算法/倍增演算法}

文章目錄 題目 解題思路 程式碼`倍增(TLE)` 程式碼`tarjan演算法(AC)` $tanjan$演算法本質上是使用並查集對“向上標記法”的優化 題目 http://acm.hdu.edu.cn

LCA 倍增模板

#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<int ,int > pii; const int N =100010; const in

LCA演算法模板

#include<string> #include<cstring> #include<iostream> #include<cstdio> #define LL long long using namespace

LCA(最近公共祖先)Tarjan演算法模板

#include <iostream> #include <cstdio> #include <cstring> #include <vector> using namespace std; /** 1.dfs 2.

最近公共祖先LCA---線上倍增演算法

  線上求LCA,多次詢問。倍增演算法時間複雜度為。   1、dfs求每個節點所在層數 void dfs(int u,int root,int d) { int i; depth[u]=d; fa[u][0]=root;//初始化 int

模板LCA Tarjan演算法模板題:洛谷P3379)

題目描述 如題,給定一棵有根多叉樹,請求出指定兩個點直接最近的公共祖先。 輸入輸出格式 輸入格式: 第一行包含三個正整數N、M、S,分別表示樹的結點個數、詢問的個數和樹根結點的序號。 接下來N-1行每行包含兩個正整數x、y,表示x結點和y結點之間有一條直接連線的邊

LCA——最近公共祖先 倍增演算法

LCA是啥呢,LCA就是一棵樹裡兩個節點的最近公共祖先,如下圖 2號節點和3號節點的LCA就是1, 5號節點和11號節點的LCA就是2,8號節點和4號節點的lca就是4 那麼怎麼求LCA呢。首先要建樹,然後最容易想到的就是兩個節點一起向上跳,第一個相遇的節點就是LCA

HDU 4757 Tree (倍增演算法LCA + 可持久化Trie樹)

題目大意: 就是現在給出一棵樹, 結點個數不超過10W, 每個節點上有一個不超過2^16的非負整數, 然後10W次詢問, 每次詢問兩個節點的路徑上的所有數中異或上給出的數的最大值 大致思路: 剛開始做這個題想的是樹鏈剖分之後用線段樹套Trie樹來做...結果悲劇地MLE了

LCA(最近公共祖先)倍增演算法

最近公共祖先有多種演算法 如倍增,RMQ,樹鏈剖分等 這裡先介紹倍增演算法 預處理複雜度nlog(n); 詢問複雜度log(n); 倍增與二進位制息息相關 與分塊的演算法有些相似之處 使用倍增演算法時開一個fa[n][S]陣列 fa[i][j]

Algorithm---LCA倍增演算法

deep[i] 表示 i節點的深度, fa[i,j]表示 i 的 2^j (即2的j次方) 倍祖先,那麼fa[i , 0]即為節點i 的父親,然後就有一個遞推式子: fa[i,j]= fa [ fa [i,j-1] , j-1 ]  可以這樣理解: 設tmp = fa [

UVa 11149 矩陣的冪(矩陣倍增模板題)

ble 化簡 .cn target ans txt put std net https://vjudge.net/problem/UVA-11149 題意: 輸入一個n×n矩陣A,計算A+A^2+A^3+...A^k的值。 思路: 矩陣倍增法。

(樹形dp+LCA倍增法)CSU 1915 - John and his farm

const http 題解 def iostream clu algo farm john 題意: 有一個棵樹,現在讓你找兩個點連接起來,這樣必然成為一個環,現在要求這些環長度的期望,也就是平均值。 分析: 第一次做LCA題,做多校的時候,瞎幾把找了模板敲,敲了個八九