• LCA


    最近公共祖先

    LCA

    Tarjan

    树剖


    最简单的(LCA)就是利用倍增的思想,(f[i][j])表示从(i)号节点往上跳(2^j)个点到哪了。

    先将两个点跳到同一高度,然后一块往上跳,最后得到的节点的父亲就是答案。

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    using namespace std;
    int n,m,s,k,head[500002],d[500002],p[500002][21];
    struct node{
    	int v,next;
    }e[2*500002];
    void add(int a,int b){
    	e[k].v=b,e[k].next=head[a],head[a]=k++;
    }
    void dfs(int u,int fa){
    	d[u]=d[fa]+1,p[u][0]=fa;
    	for(int i=1;(1<<i)<=d[u];i++) p[u][i]=p[p[u][i-1]][i-1];
    	for(int i=head[u],v;v=e[i].v,i!=-1;i=e[i].next)
    		if(v!=fa) dfs(v,u);
    }
    int lca(int a,int b){
        if(d[a]>d[b]) swap(a,b);
        for(int i=20;i>=0;i--)
            if(d[a]<=d[b]-(1<<i)) b=p[b][i];
    	if(a==b) return a;
    	for(int i=20;i>=0;i--)
    		if(p[a][i]==p[b][i]) continue;
    		else a=p[a][i],b=p[b][i];
    	return p[a][0];
    }
    int main(){
        memset(head,-1,sizeof(head));
        scanf("%d%d%d",&n,&m,&s);
        int a,b;
        for(int i=1;i<n;i++){
            scanf("%d%d",&a,&b);
            add(a,b),add(b,a);
        }
        dfs(s,0);
        for(int i=1;i<=m;i++){
            scanf("%d%d",&a,&b);
            printf("%d
    ",lca(a,b));
        }
        return 0;
    }
    

    (Tarjan~LCA)

    这是一种离线算法。

    思想就是先将所有边和所有问题存下来,先(dfs),将子节点与父亲节点合并,返回的时候顺便记录一下答案。因为每棵子树连续遍历,所以返回的时候找到的爸爸就是两点的最近公共祖先。

    #include<iostream>
    #include<cstdlib>
    #include<cctype>
    using namespace std;
    int fa[500010],head[500010],qhead[500010],que[500010],cnt,x,y,n,m,s;
    struct Edge{//树
    	int next,to;
    }edge[1000010];
    struct qEdge{//问题
    	int next,to,ans=0;
    }q[1000010];
    void add(int x,int y){//树
    	edge[++cnt].to=y,edge[cnt].next=head[x],head[x]=cnt;
    }
    void qadd(int x,int y,int k){//问题
    	q[k].to=y,q[k].next=qhead[x],qhead[x]=k;
    }
    
    //并查集
    int find(int x){
    	return fa[x]!=x? fa[x]=find(fa[x]):fa[x];
    }
    void unionn(int x,int y){
    	x=find(x),y=find(y);
    	fa[y]=x;
    }
    
    void tarjan(int x){
    	que[x]=1;
    	for(int i=head[x],to;to=edge[i].to,i;i=edge[i].next)
    		if(!que[to]){
    			tarjan(to);
    			unionn(x,to);//将子节点向父节点合并
    		}
    	for(int i=qhead[x],to;i,to=q[i].to;i=q[i].next)
    		if(que[to]==2){//记录答案
    			q[i].ans=find(to);
    			if(i%2)	q[i+1].ans=q[i].ans;//鬼畜写法
    			else q[i-1].ans=q[i].ans;
    		}
    	que[x]=2;//表示搜完了
    }
    int main(){
    	cin>>n>>m>>s;
    	for(int i=1;i<n;++i){
    		cin>>x>>y;
    		add(x,y);add(y,x);
    		fa[i]=i;
    	}
    	fa[n]=n;
    	for(int i=1;i<=m;++i){
    		cin>>x>>y;
    		qadd(x,y,i*2-1);qadd(y,x,i*2);
    	}
    	tarjan(s);
    	for(int i=1;i<=n;++i)
    		cout<<q[i*2].ans;
    	return 0;
    }
    

    第三种好写不长也同样很好理解的方式是树剖。比前面两种方法的代码都短。

    树剖详解链接

    这里简单提一句吧。就是两边dfs记录每条链的top,只要两个点不在一条连上,就往链头的父亲上跳,直到跳到同一条链上,这是谁的深度小谁就是答案。感觉比前两个要快

    #include <iostream>
    #include <cstdio>
    using namespace std;
    long long read() {
      long long x = 0; int f = 0; char c = getchar();
      while (c < '0' || c > '9') f |= c == '-', c = getchar();
      while (c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
      return f ? -x : x;
    }
    
    int n, m, s;
    int hd[500005], cnt, son[500005], top[500005],w[500005], dep[500005], f[500005];
    struct szh {
      int to, nxt;
    }a[1000005];
    void add(int x, int y) {
      a[++cnt].to = y, a[cnt].nxt = hd[x], hd[x] = cnt;
    }
    void dfs(int u, int F) {
      dep[u] = dep[F] + 1; w[u] = 1; f[u] = F;
      for (int i = hd[u], v; v = a[i].to, i; i = a[i].nxt) {
        if (v == F) continue;
        dfs(v, u);
        w[u] += w[v];
        if (!son[u] || w[v] > w[son[u]]) son[u] = v;
      }
    }
    void dfs(int u, int tp, int F) {
      top[u] = tp;
      if (son[u]) dfs(son[u], tp, u);
      for (int i = hd[u], v; v = a[i].to, i; i = a[i].nxt) {
        if (v == F || v == son[u]) continue;
        dfs(v, v, u);
      }
    }
    int main() {
      n = read(); m = read(); s = read();
      for (int i = 1, x, y; i < n; ++i) {
        x = read(); y = read(); add(x, y); add(y, x);
      }
      dfs(s, 0); dfs(s, s, 0);
      int a, b; 
      while (m--) {
        a = read(); b = read();
        while (top[a] != top[b]) 
          if (dep[top[a]] >= dep[top[b]]) a = f[top[a]];
          else b = f[top[b]];
        printf("%d
    ", dep[a] > dep[b] ? b : a);
      }
      return 0;
    }
    

    欢迎指正评论O(∩_∩)O~~

  • 相关阅读:
    redis sentinel(哨兵)配置解读
    配置哨兵监控Redis运行情况
    java 客户端链接不上redis解决方案
    Redis配置主从架构,实现读写分离
    Redis简介,安装和配置,停止,卸载(图解方式)
    linux下监控用户的操作记录---录像播放性质
    Spring的数据库开发
    Spring中Bean的作用域、生命周期
    Spring中Bean的实例化
    Spring之初体验
  • 原文地址:https://www.cnblogs.com/kylinbalck/p/9878895.html
Copyright © 2020-2023  润新知