(详细)LCA板子以及入门题总结

LCA算法总结

把lca题目记入下来!分享给你们

板子tarjan(离线)

#define N 10005
#define M 1000005
// N 表示的树的节点
// M 表示的问题数 
int cnt_e,cnt_q,head_e[N],head_q[N],vis[N],dis[N],t,ans[M],id[N],f[N];
// 边,问题数 
struct node{
    int u,w,next;
}e[N*2],q[M*2];
void init(){
    memset(head_e,-1,sizeof(head_e));
    memset(head_q,-1,sizeof(head_q));
    memset(vis,0,sizeof(vis));
    cnt_e=0;	
    cnt_q=0;
}
void addedge(int x,int y,int z){
    e[cnt_e].u=x;e[cnt_e].w=z;e[cnt_e].next=head_e[y];head_e[y]=cnt_e++;
    e[cnt_e].u=y;e[cnt_e].w=z;e[cnt_e].next=head_e[x];head_e[x]=cnt_e++;
}
void addquery(int x,int y,int z){
    q[cnt_q].u=x;q[cnt_q].w=z;q[cnt_q].next=head_q[y];head_q[y]=cnt_q++;
    q[cnt_q].u=y;q[cnt_q].w=z;q[cnt_q].next=head_q[x];head_q[x]=cnt_q++;
}
int find(int x){
    if(f[x] == x)
    return x;
    else
    return f[x] = find(f[x]);
}
void tarjan(int k){
    f[k]=k;
    vis[k]=1;
    id[k]=t;
    int i,v;
    for(i=head_q[k];i!=-1;i=q[i].next){
        v=q[i].u;
        if(vis[v]){
        	
			// 表示的就是一颗树 
			// 每次只需要在这里做文章就行
			
			// ans[q[i].w] = find(v);      				//求的是lca 
        	//ans[q[i].w]=dis[k]+dis[v]-2*dis[find(v)];	//求的是两个点的距离 
			
			 
			//  solve1() 可以表示的是森林 
			//可以去求这个森林 
			
            if(id[v] == id[k])
            //ans[q[i].w] = find(v);
            ans[q[i].w] = dis[k]+dis[v]-2*dis[find(v)];
            else
            ans[q[i].w]=-1;
            
        }
    }
    for(i=head_e[k];i!=-1;i=e[i].next){
        v=e[i].u;
        if(!vis[v]){
            dis[v]=dis[k]+e[i].w;
            tarjan(v);
            f[v]=k;
        }
    }
}

----------------------------------------分割线-----------------------------------

LCA第一题:

给定一棵有根多叉树,请求出指定两个点直接最近的公共祖先
洛谷3379
题意:-------
我的理解:
直接套板子(因为是一颗树!直接套solve2)

AC 代码

#include<bits/stdc++.h>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cctype>
#include<cstdlib>
#include<cstring>
#include<string>
#include<set>

typedef long long int ll;

using namespace std;

void read(int &x){
    	int f=1;x=0;char s=getchar();
    	while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    	while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    	x*=f;
}
#define N 500005
#define M 1000005
// N 表示的树的节点
// M 表示的问题数 
int cnt_e,cnt_q,head_e[N],head_q[N],vis[N],dis[N],t,ans[M],id[N],f[N];
// 边,问题数 
struct node{
    int u,w,next;
}e[N*2],q[M*2];
void init(){
    memset(head_e,-1,sizeof(head_e));
    memset(head_q,-1,sizeof(head_q));
    memset(vis,0,sizeof(vis));
    cnt_e=0;	
    cnt_q=0;
}
void addedge(int x,int y,int z){
    e[cnt_e].u=x;e[cnt_e].w=z;e[cnt_e].next=head_e[y];head_e[y]=cnt_e++;
    e[cnt_e].u=y;e[cnt_e].w=z;e[cnt_e].next=head_e[x];head_e[x]=cnt_e++;
}
void addquery(int x,int y,int z){
    q[cnt_q].u=x;q[cnt_q].w=z;q[cnt_q].next=head_q[y];head_q[y]=cnt_q++;
    q[cnt_q].u=y;q[cnt_q].w=z;q[cnt_q].next=head_q[x];head_q[x]=cnt_q++;
}
int find(int x){
    if(f[x] == x)
    return x;
    else
    return f[x] = find(f[x]);
}
void tarjan(int k){
    f[k]=k;
    vis[k]=1;
    id[k]=t;
    int i,v;
    for(i=head_q[k];i!=-1;i=q[i].next){
        v=q[i].u;
        if(vis[v]){
        	
			// 表示的就是一颗树 
			// 每次只需要在这里做文章就行
			ans[q[i].w] = find(v);      				//求的是lca 
        	//ans[q[i].w]=dis[k]+dis[v]-2*dis[find(v)];	//求的是两个点的距离 
			
			 
			//  solve1() 可以表示的是森林 
			//可以去求这个森林 
			/*
            
            //ans[q[i].w] = find(v);
            ans[q[i].w] = dis[k]+dis[v]-2*dis[find(v)];
            else
            ans[q[i].w]=-1;
            */
        }
    }
    for(i=head_e[k];i!=-1;i=e[i].next){
        v=e[i].u;
        if(!vis[v]){
            dis[v]=dis[k]+e[i].w;
            tarjan(v);
            f[v]=k;
        }
    }
}

void solve1(){
	int n, m, cc, root, x, y, z;
	while(scanf("%d%d%d",&n,&cc,&m) == 3){
		init();
		while(cc--){
			//scanf("%d%d%d",&x,&y,&z);
        	read(x),read(y),read(z);
        	//z = 1;
        	addedge(x,y,z);
		}
		for(int i = 0;i < m; i++){
        	read(x),read(y);
			//scanf("%d%d",&x,&y);
        	addquery(x,y,i);
    	}
    	t = 0;    // 这个是全局的变量
		for(int i = 1; i <= n; i++, t++){
			if(!vis[i]){
				dis[i] = 0;
				tarjan(i);
			}
		} 
		for(int i = 0; i < m; i++){
			if(ans[i] == -1){					//				表示的就是不在一颗树 
				puts("Not connected");
			}else
				printf("%d\n",ans[i]);
		}
	} 
}
void solve2(){
	
	// 需要注意的就是这个tarjan 里面ans换下就可以求一颗树
	
	int tt ;
	
	//read(tt);
	tt = 1;
	while(tt--){
		int n, m, cc, root, x, y, z;
		read(n),read(m),read(root);
		init();
		for(int i = 1; i < n; i++){
			read(x),read(y);
			//read(z);
			z = 1;
			//scanf("%d%d%d",&x,&y,&z);
			addedge(x,y,z);
		} 
		for(int i = 0; i < m; i++){
			read(x),read(y);
			addquery(x,y,i);
		}
		// 一颗树的话,一般root 为1 ///只有一颗树,t 默认为0
		// root = 1;
		t = 0;  
		dis[root] = 0;
		tarjan(root);
		for(int i = 0; i < m; i++){
			printf("%d\n",ans[i]);
		} 
	}
}
int main(){
    
    int tt;
    //read(tt);
    tt = 1;
    while(tt--){
    	// 1 表示的就是森林 
		//solve1();
    	
		solve2();
	} 
	return 0;    
}

LCA第二题:

给你一个森林!判断两点距离!
Hdu
题意:
我的理解:
直接去tarjan离线!!!容易TLE!

AC 代码

#include<bits/stdc++.h>
typedef long long int ll;

using namespace std;

void read(int &x){
    	int f=1;x=0;char s=getchar();
    	while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    	while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    	x*=f;
}
#define N 10005
#define M 1000005
// N 表示的树的节点
// M 表示的问题数 
int cnt_e,cnt_q,head_e[N],head_q[N],vis[N],dis[N],t,ans[M],id[N],f[N];
// 边,问题数 
struct node{
    int u,w,next;
}e[N*2],q[M*2];
void init(){
    memset(head_e,-1,sizeof(head_e));
    memset(head_q,-1,sizeof(head_q));
    memset(vis,0,sizeof(vis));
    cnt_e=0;	
    cnt_q=0;
}
void addedge(int x,int y,int z){
    e[cnt_e].u=x;e[cnt_e].w=z;e[cnt_e].next=head_e[y];head_e[y]=cnt_e++;
    e[cnt_e].u=y;e[cnt_e].w=z;e[cnt_e].next=head_e[x];head_e[x]=cnt_e++;
}
void addquery(int x,int y,int z){
    q[cnt_q].u=x;q[cnt_q].w=z;q[cnt_q].next=head_q[y];head_q[y]=cnt_q++;
    q[cnt_q].u=y;q[cnt_q].w=z;q[cnt_q].next=head_q[x];head_q[x]=cnt_q++;
}
int find(int x){
    if(f[x] == x)
    return x;
    else
    return f[x] = find(f[x]);
}
void tarjan(int k){
    f[k]=k;
    vis[k]=1;
    id[k]=t;
    int i,v;
    for(i=head_q[k];i!=-1;i=q[i].next){
        v=q[i].u;
        if(vis[v]){
        	
			// 表示的就是一颗树 
			// 每次只需要在这里做文章就行
			//ans[q[i].w] = find(v);      				//求的是lca 
        	//ans[q[i].w]=dis[k]+dis[v]-2*dis[find(v)];	//求的是两个点的距离 
			
			 
			//  solve1() 可以表示的是森林 
			//可以去求这个森林 
			
            
            //ans[q[i].w] = find(v);
            if(id[v] == id[k])
            ans[q[i].w] = dis[k]+dis[v]-2*dis[find(v)];
            else
            ans[q[i].w]=-1;
            
        }
    }
    for(i=head_e[k];i!=-1;i=e[i].next){
        v=e[i].u;
        if(!vis[v]){
            dis[v]=dis[k]+e[i].w;
            tarjan(v);
            f[v]=k;
        }
    }
}

void solve1(){
	int n, m, cc, root, x, y, z;
	
	while(scanf("%d%d%d",&n,&cc,&m) == 3){
		init();
		while(cc--){
			//scanf("%d%d%d",&x,&y,&z);
        	read(x),read(y),read(z);
        	//z = 1;
        	addedge(x,y,z);
		}
		for(int i = 0;i < m; i++){
        	read(x),read(y);
			//scanf("%d%d",&x,&y);
        	addquery(x,y,i);
    	}
    	t = 0;    // 这个是全局的变量
		for(int i = 1; i <= n; i++, t++){
			if(!vis[i]){
				dis[i] = 0;
				tarjan(i);
			}
		} 
		for(int i = 0; i < m; i++){
			if(ans[i] == -1){					//				表示的就是不在一颗树 
				puts("Not connected");
			}else
				printf("%d\n",ans[i]);
		}
	} 
}
void solve2(){
	
	// 需要注意的就是这个tarjan 里面ans换下就可以求一颗树
	
	int tt ;
	
	//read(tt);
	tt = 1;
	while(tt--){
		int n, m, cc, root, x, y, z;
		read(n),read(m),read(root);
		init();
		for(int i = 1; i < n; i++){
			read(x),read(y);
			//read(z);
			z = 1;
			//scanf("%d%d%d",&x,&y,&z);
			addedge(x,y,z);
		} 
		for(int i = 0; i < m; i++){
			read(x),read(y);
			addquery(x,y,i);
		}
		// 一颗树的话,一般root 为1 ///只有一颗树,t 默认为0
		// root = 1;
		t = 0;  
		dis[root] = 0;
		tarjan(root);
		for(int i = 0; i < m; i++){
			printf("%d\n",ans[i]);
		} 
	}
}
int main(){
    
    int tt;
    //read(tt);
    tt = 1;
    while(tt--){
    	// 1 表示的就是森林 
		solve1();
    	
		//solve2();
	} 
	return 0;    
}

持续更新中

猜你喜欢

转载自blog.csdn.net/qq_43127921/article/details/89318166