1. 程式人生 > >LCA演算法 線上樹上倍增模板

LCA演算法 線上樹上倍增模板

測試資料

1 10 1
1 2 2
1 4 4
2 3 3
2 5 6
3 7 1
3 8 2
5 6 3
6 9 2 
4 10 4
9 10

終於造了什麼事樹上倍增了下午考pat。。。哭卿卿

程式碼理解來自 自己又加了備註。。。。 建議模擬一下

https://blog.csdn.net/a601025382s/article/details/10615039 

#pragma comment(linker, "/STACK:1024000000,1024000000")  
#include<bits/stdc++.h> 
using namespace std;
const int maxn=40004;
struct node{
    int to,w;
    node(int a=0,int b = 0){to=a;w=b;}
};
vector<node>e[maxn];
int f[maxn],dis[maxn],deep[maxn],p[maxn][20],n;
void dfs(int u,int pre,int t)
{
    int i,num;
    deep[u]=t;//深度
    f[u]=pre;//父節點
    num=e[u].size();
    for(i=0;i<num;i++)
    {
        int v=e[u][i].to;
        if(v!=pre)
        {
            dis[v]=dis[u]+e[u][i].w;//距離跟的距離
            dfs(v,u,t+1);
        }
    }
}
void init()
{
    //p[i][j]表示i結點的第2^j祖先
    int i, j;
    for(j = 1;(1 << j) <= n; j++)
        for(i = 1;i <= n; i++)
        p[i][j] = -1; //都初始化為-1
    for(i = 1;i <= n; i++)
		p[i][0] = f[i];
    for(j = 1;(1<<j) <= n;j ++)
    {
    	for(i = 1;i <= n; 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)//最近公共祖先
{
    int i,j;
    if(deep[a] < deep[b])
		swap(a,b);
    for(i = 0;(1 << i) <= deep[a]; i++);
    	i--;//其實小於也行等於也好 
    //使a,b兩點的深度相同
    for(j = i;j >= 0;j--)
    {
		if(deep[a]-(1<<j)>=deep[b])
        {
			a = p[a][j];
		}
    } //找到與b同層的或者說 b沒有同層的 那麼就找他的上一層 不用擔心p[a][j]是否等於-1 肯定有 
    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];
        }
    }
    /*如果說都是相等的話 那麼說明a b在兩個子樹的左右子樹下面 所以直接返回一個子樹的根節點就行  
      如果說是有不相等的話 那麼就兩個節點各自更新成他們的p[a][j] 和 p[b][j] 
    */ 
    return f[a]; 
 
}
int main()
{
    int m,i,a,b,c,ans;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)e[i].clear();
    for(i=1;i<n;i++)
    {
        scanf("%d%d%d",&a,&b,&c);
        e[a].push_back(node(b,c));
        e[b].push_back(node(a,c));
    }
    dis[1]=0;
    dfs(1,-1,0);//找到各點的深度和各點的父節點以及距離根的距離
//	cout<<"dis"<<endl;
//	for(int i = 0 ; i <= n ; i ++)
//		printf("%d%c",dis[i]," \n"[i==n]);
//	cout<<"deep"<<endl;
//	for(int i = 0 ; i <= n ; i ++)
//		printf("%d%c",deep[i]," \n"[i==n]);
//	cout<<"f"<<endl;
//	for(int i = 0 ; i <= n ; i ++)
//		printf("%d%c",f[i]," \n"[i==n]);
    init();     //初始各個點的2^j祖先是誰    
    for(i=0;i<m;i++)
    {
        scanf("%d%d",&a,&b);
        cout<<lca(a,b)<<endl;
    }
    return 0;
}
/*
    最近公共祖先lca,線上演算法/倍增法,模板題。套別人模板自己敲了遍,現在還要回顧下鄰接表,哎。。
    用vector,發現爆棧了,汗一個。。
    用#pragma comment(linker, "/STACK:1024000000,1024000000") 開個把棧開大點吧。。hdu可以,別的地方就不清楚了
*/
/*
1 10 7
1 2 2
1 4 4
2 3 3
2 5 6
3 7 1
3 8 2
5 6 3
6 9 2 
4 10 4
*/