倍增LCA
阿新 • • 發佈:2018-05-30
str -c urn 父親 adc main 要求 模板 ++
題目描述 如題,給定一棵有根多叉樹,請求出指定兩個點直接最近的公共祖先。 輸入輸出格式 輸入格式: 第一行包含三個正整數N、M、S,分別表示樹的結點個數、詢問的個數和樹根結點的序號。 接下來N-1行每行包含兩個正整數x、y,表示x結點和y結點之間有一條直接連接的邊(數據保證可以構成樹)。 接下來M行每行包含兩個正整數a、b,表示詢問a結點和b結點的最近公共祖先。 輸出格式: 輸出包含M行,每行包含一個正整數,依次為每一個詢問的結果。 輸入輸出樣例 輸入樣例#1: 復制 5 5 4 3 1 2 4 5 1 1 4 2 4 3 2 3 5 1 2 4 5 輸出樣例#1: 復制luogu 3379 lca模板4 4 1 4 4 說明 時空限制:1000ms,128M 數據規模: 對於30%的數據:N<=10,M<=10 對於70%的數據:N<=10000,M<=10000 對於100%的數據:N<=500000,M<=500000 樣例說明: 該樹結構如下: 第一次詢問:2、4的最近公共祖先,故為4。 第二次詢問:3、2的最近公共祖先,故為4。 第三次詢問:3、5的最近公共祖先,故為1。 第四次詢問:1、2的最近公共祖先,故為4。 第五次詢問:4、5的最近公共祖先,故為4。 故輸出依次為4、4、1、4、4。
為什麽要用倍增來求呢?
因為快啊,不用一個一個跳,直接以2的冪次方跳。
那我們講解一下大致過程:
首先我們需要每個點的深度和它往上2的冪次方的節點是哪個;
這個可以用dfs和dp搞出來;
dp【i】【j】表示第i個節點往上跳2的j次方後的節點是哪個;
那麽有轉移dp【i】【j】=dp【dp【i】【j-1】】【j-1】;
然後我們求lca
首先我們要使第一個點是深度大的那個,因為這樣好寫
其次我們枚舉深度(2^k),當2^k<=這兩個點之間的深度差的最大值時;
我們讓x向上跳2^k;
這個地方有一個特判,在代碼裏有,懶得再打一遍了;
然後倒序繼續枚舉深度,如果兩個點都跳2的k次方後為同一個點,那就不用跳了,因為是倒敘枚舉,後面肯定要比前面優,如果不是同一個點,就跳;
最後跳完肯定是兩個點都在lca的下面一個點;
那麽只要求這個點的父親返回就是lca;
下面 ↓
↓
↓
↓
↓
↓
↓
↓
↓
↓
↓
#include<cstdio> #include<algorithm> #include<iostream> #include<cstring> using namespace std; int f[500050][50],ans[5000000],head[5000000],deep[5000000],cnt,root,n,m,tt,flag,yqy;//f[i][j]表示第i個節點向上跳2的j次方的節點 struct node { int to,next; }edge[1000500]; void add(int x,int y) { cnt++; edge[cnt].to=y; edge[cnt].next=head[x]; head[x]=cnt; } void dfs(int x,int father){ deep[x]=deep[father]+1; f[x][0]=father;//向上一個節點就是該點的父親; for(int i=1;(1<<i)<=deep[x];i++) f[x][i]=f[f[x][i-1]][i-1];//x向上跳2^i可以由x先向上跳2^(i-1)再跳2^(i-1)轉移過來;不是2的i-1次方的i-1次方; for(int i=head[x];i;i=edge[i].next){ int y=edge[i].to; if(y!=father)dfs(y,x); } } int LCA(int x,int y) { if(deep[x]<deep[y]) { int temp=x; x=y; y=temp; }//保證x為深度大的; for(int i=20;i>=0;i--) if(deep[x]-deep[y]>=(1<<i))//如果兩個節點的深度差比2的i次方大; x=f[x][i];//x就往上跳2的i次方; if(x==y) //如果跳完正好在同一個節點,那麽就返回這個節點; return x; for(int k=20;k>=0;k--) if(f[x][k]!=f[y][k])//如果兩個點都跳2的k次方後為同一個點,那就不用跳了,因為是倒敘枚舉,後面肯定要比前面優,如果不是同一個點,就跳; x=f[x][k],y=f[y][k]; return f[y][0];//跳完之後肯定保留到兩點只跳一步就相同,所以返回父親就行了; } int main() { // ios::sync_with_stdio(0); int x,y,a,b; scanf("%d%d%d",&n,&m,&root); for(int i=1;i<=n-1;i++) { scanf("%d%d",&x,&y); add(x,y); add(y,x); } dfs(root,0); for(int i=0;i<m;i++) { scanf("%d%d",&a,&b); printf("%d\n",LCA(a,b)); } return 0; }標程加解析
可以轉載,請註明來源
倍增LCA