• 【模板】最近公共祖先【LCA】


    ##题目大意:
    题目链接:https://www.luogu.org/problemnew/show/P3379
    给出一个树和mm组询问,对于每个询问输出两个结点的LCALCA


    ##思路:
    思路一:树上倍增
    树上倍增是LCALCA的基本方法之一。其做法是先将xxyy跳到统一深度上,再利用倍增思想找到两点的LCALCA
    所以要先用DFSDFS求出每个点的深度以及它的祖宗,然后在对于每一询问完成上述操作即可。


    思路二:STST
    对于一棵树上的两个不同节点xxyy,从根开始跑一遍欧拉序,设xx在欧拉序中第一次出现的位置为first[x]first[x]yy第一出现的位置是first[y]first[y],那么xxyyLCALCA就是欧拉序中first[x]first[x]first[y]first[y]之间深度最低的(即min(dep[i])min(dep[i]))节点就是它们的LCALCA
    证明略。但是很明显是正确的。
    所以,先跑一边DFSDFS求出first[i]first[i]和欧拉序,再利用STST表求出任意区间的最小值(LCALCA),然后再读入输出即可。
    注意!此方法时间较慢,需要输入输出流才能过!


    ##代码:
    树上倍增:

    
    #include <cstdio>
    #include <iostream>
    #include <cmath>
    #include <algorithm>
    #define N 500030
    #define LG 25
    using namespace std;
    
    int dep[N+1],head[N+1],lg[LG+1],k,n,m,s,x,y,f[N+1][LG+1];
    
    struct edge  //邻接表
    {
    	int to,next;
    }e[N<<1+1];
    
    void add(int from,int to)
    {
    	e[++k].to=to;
    	e[k].next=head[from];
    	head[from]=k;
    }
    
    void dfs(int x,int fa)  //求每个点的深度
    {
    	dep[x]=dep[fa]+1;
    	f[x][0]=fa;
    	for (int i=1;(1<<i)<=dep[x];i++)
    	 f[x][i]=f[f[x][i-1]][i-1];  //树上倍增祖宗
    	for (int i=head[x];i;i=e[i].next)
    	 if (e[i].to!=fa)
    	  dfs(e[i].to,x);
    }
    
    int lca(int x,int y)  //倍增
    {
    	if (dep[y]>dep[x]) swap(x,y);
    	for (int i=LG;i>=0;i--)
    	 if (dep[f[x][i]]>=dep[y]) x=f[x][i];  //到达同一高度
    	if (x==y) return x;
    	for (int i=LG;i>=0;i--)  //倍增
    	 if (f[x][i]!=f[y][i])
    	 {
    	 	x=f[x][i];
    	 	y=f[y][i];
    	 }
    	return f[x][0];
    }
    int main()
    {
    	//freopen("lca.in","r",stdin);
    	scanf("%d%d%d",&n,&m,&s);
    	for (int i=1;i<n;i++)
    	{
    		scanf("%d%d",&x,&y);
    		add(x,y);
    		add(y,x);
    	}
    	lg[1]=0;lg[2]=1;
    	for (int i=3;i<=LG;i++)
    	 lg[i]=lg[i>>1]+1;  //预处理
    	dfs(s,0);
    	for (int i=1;i<=m;i++)
    	{
    		scanf("%d%d",&x,&y);
    		printf("%d\n",lca(x,y));
    	}
    	return 0;
    }
    
    

    STST表:

    #include <cstdio>
    #include <cmath>
    #include <cstring>
    #include <algorithm>
    #define N 500020
    #define LG 20
    using namespace std;
    
    int first[N<<1+1],dep[N<<1+1],vis[N<<1+1],rmq[N<<1+1][LG],num[N<<1+1][LG],head[N<<1/+1];
    int n,m,s,k,sum,x,y,z;
    
    struct edge
    {
    	int next,to;
    }e[N<<1+1];
    
    int f;
    char c;
    
    int read()  //输入流
    {
        f=0;
        while(c=getchar(),c<=47||c>=58);f=(f<<3)+(f<<1)+c-48;
        while(c=getchar(),c>=48&&c<=57) f=(f<<3)+(f<<1)+c-48;
        return f;
    }
    
    void write(int x)  //输出流(可以不用)
    {
        if(x>9) write(x/10);else putchar(x%10+48);
        return;
    }
    
    void add(int from,int to)
    {
    	k++;
    	e[k].to=to;
    	e[k].next=head[from];
    	head[from]=k;
    }
    
    void dfs(int x,int k)  //求欧拉序
    {
    	sum++;
    	first[x]=sum;
    	dep[sum]=k;
    	vis[sum]=x;
    	for (int i=head[x];~i;i=e[i].next)
    	{
    		int v=e[i].to;
    		if (first[v]) continue;  //这个点已经到达过
    		dfs(v,k+1);
    		sum++;
    		dep[sum]=k;
    		vis[sum]=x;
    	}
    	return;
    }
    
    int main()
    {
    	//freopen("q.txt","r",stdin);
    	memset(head,-1,sizeof(head));
    	//scanf("%d%d%d",&n,&m,&s);
    	n=read();
    	m=read();
    	s=read();
    	for (int i=1;i<n;i++)
    	{
    		//scanf("%d%d",&x,&y);
    		x=read();
    		y=read();
    		add(x,y);
    		add(y,x);
    	}
    	
    	dfs(s,1);
    	
    	for (int i=1;i<=sum;i++)  //预处理
    	{
    		rmq[i][0]=dep[i];
    		num[i][0]=vis[i];
    	}
    	for (int j=1;j<=log2(sum);j++)
    	 for (int i=1;i+(1<<j)-1<=sum;i++)  //ST表,RMQ
    	  if (rmq[i][j-1]<rmq[i+(1<<(j-1))][j-1])
    	  {
    	  	 rmq[i][j]=rmq[i][j-1];
    	  	 num[i][j]=num[i][j-1];
    	  }
    	  else
    	  {
    	  	 rmq[i][j]=rmq[i+(1<<(j-1))][j-1];
    	  	 num[i][j]=num[i+(1<<(j-1))][j-1];
    	  }	  
    	for (int i=1;i<=m;i++)
    	{
    		//scanf("%d%d",&x,&y);
    		x=read();
    		y=read();
    		if (first[x]>first[y]) swap(x,y);
    		x=first[x];
    		y=first[y];
    		z=log2(y-x+1);  //求log
    		if (rmq[x][z]>rmq[y-(1<<z)+1][z]) 
    		 printf("%d\n",num[y-(1<<z)+1][z]);
    		else printf("%d\n",num[x][z]);
       	    //putchar(10);
    	}
    	getchar();getchar();
    	return 0;
    }
    
  • 相关阅读:
    php解决与处理网站高并发 大流量访问的方法
    mysql事务和锁InnoDB
    从一个死锁看mysql innodb的锁机制
    Git如何删除自己创建的项目
    公众号的坑
    字符串转Unicode码
    字符串转UTF-8码(%开头)
    git介绍和使用
    ng2中文文档地址
    两个数组的排序方法
  • 原文地址:https://www.cnblogs.com/hello-tomorrow/p/11998752.html
Copyright © 2020-2023  润新知