zoukankan      html  css  js  c++  java
  • LCA

    Tarjan

    
    #include<iostream>
    #include<queue>
    #include<list>
    #include<vector>
    #include<cstring>
    #include<set>
    #include<stack>
    #include<map>
    #include<cmath>
    #include<algorithm>
    #include<string>
    #include<stdio.h>
    using namespace std;
    typedef long long ll;
    #define MS(x,i) memset(x,i,sizeof(x))
    #define rep(i,s,e) for(int i=s; i<=e; i++)
    #define sc(a) scanf("%d",&a)
    #define scl(a) scanf("%lld",&a)
    #define sc2(a,b) scanf("%d %d", &a, &b)
    #define debug printf("debug......
    ");
    #define pfd(x) printf("%d
    ",x)
    #define pfl(x) printf("%lld
    ",x)
    const double eps=1e-8;
    const double PI = acos(-1.0);
    const int inf = 0x3f3f3f3f;
    const ll INF = 0x7fffffff;
    const int maxn = 4e4+10;
    const int M = 2e2+10;
    int dx[4] = {0, 0, 1, -1};
    int dy[4]  = {1, -1, 0 , 0};
    
    int n,q;//n个顶点 q次询问
    int lca[maxn];//lca[i]表示第i个询问的结果 祖先
    int dist[maxn];//dist[i]表示i到根节点的距离
    
    //对树建图
    int head[maxn];
    int cnt;
    struct node{
        int v,nxt,w;
    }edge[maxn*2];
    
    //对询问建图
    int headq[maxn];
    int cntq;
    struct Node{
        int u,v;//起点和终点
        int nxt;//同起点的下一个询问的位置
        int id;//记下是第几次询问
    }query[M*2];
    
    bool vis[maxn];//访问标志
    //树加边
    void addEdge(int u, int v, int w){
        edge[cnt].v = v;
        edge[cnt].w = w;
        edge[cnt].nxt = head[u];
        head[u] = cnt++;
    }
    //询问加边
    void addQ(int u, int v, int idx){
        query[cntq].u = u;
        query[cntq].v = v;
        query[cntq].nxt = headq[u];
        query[cntq].id = idx;
        headq[u] = cntq++;
    }
    //并查集基本操作
    int fa[maxn];
    void init(){
        rep(i,1,n+5){
            fa[i] = i;
            vis[i] = 0;
            head[i] = 0;
            headq[i] = 0;
        } 
        dist[1] = 0;
        cnt = 1;
        cntq = 1;
    }
    
    int find(int x){
        if(x != fa[x]){
            return fa[x] = find(fa[x]);
        }
        return x;
    }
    void merge(int x, int y){
        int fx = find(x);
        int fy = find(y);
        if(fx != fy) fa[fy] = fx;
    }
    
    //tarjan
    void tarjan(int u){
        vis[u] = 1;
        for(int i=head[u]; i; i=edge[i].nxt){
            int v = edge[i].v;
            int w = edge[i].w;
            if(!vis[v]){
                dist[v] = w + dist[u];//沿途求出每个结点到root的距离 root=1
                tarjan(v);
                merge(u , v);//v回溯,u是v的父亲
            }
        }
        //u结束 u回溯,把与u有查询关系的能更新的都更新了
        for(int i=headq[u]; i; i=query[i].nxt){
            int v = query[i].v;
            if(vis[v]) lca[query[i].id] = find(v);
        }
    }
    
    int t;
    int main(){
        sc(t);
        while(t--){
            
            sc2(n,q);
            init();//初始化不能忘记
            int u,v,w;
            rep(i,1,n-1){
                sc2(u,v);
                sc(w);
                addEdge(u,v,w);
                addEdge(v,u,w);
            }
            rep(i,1,q){
                sc2(u,v);
                addQ(u,v,i);
                addQ(v,u,i);
            }
            tarjan(1);
            //事实上可以只选出奇数或者偶数次序的询问因为 (1,2) (3,4) (5,6)...括号内是等价的
            rep(i,1,cntq-1){
                i++;
                u = query[i].u;
                v = query[i].v;
                int idx = query[i].id;
                pfd(dist[u]+dist[v]-2*dist[lca[idx]]);
                
            }
        }
    
    
        return 0;
    }
    
    
    • 倍增算法
    //LCA 倍增 大家一起跳跳跳
    #include<iostream>
    #include<queue>
    #include<list>
    #include<vector>
    #include<cstring>
    #include<set>
    #include<stack>
    #include<map>
    #include<cmath>
    #include<algorithm>
    #include<string>
    #include<stdio.h>
    using namespace std;
    typedef long long ll;
    #define MS(x,i) memset(x,i,sizeof(x))
    #define rep(i,s,e) for(int i=s; i<=e; i++)
    #define sc(a) scanf("%d",&a)
    #define scl(a) scanf("%lld",&a)
    #define sc2(a,b) scanf("%d %d", &a, &b)
    #define debug printf("debug......
    ");
    #define pfd(x) printf("%d
    ",x)
    #define pfl(x) printf("%lld
    ",x)
    const double eps=1e-8;
    const double PI = acos(-1.0);
    const int inf = 0x3f3f3f3f;
    const ll INF = 0x7fffffff;
    const int maxn = 1e4+10;
    int dx[4] = {0, 0, 1, -1};
    int dy[4]  = {1, -1, 0 , 0};
    
    int t,n;
    vector<int> G[maxn];
    int in[maxn];//记录入度
    int s,e,lca;
    int depth[maxn];//记录每个结点的深度 根节点深度为0
    int fa[maxn][20];//fa[i][j]表示i结点向上爬 2^j是哪个结点
    
    //预处理出每个结点的深度和其直接父节点
    void dfs(int u, int pre, int d){
        fa[u][0] = pre;//向上一个当然是直接父节点 pre
        depth[u] = d;//深度
        for(int i=0; i<G[u].size(); i++){
            int v = G[u][i];
            if(v != pre){
                dfs(v , u, d+1);
            }
        }
    }
    //倍增预处理出fa数组
    void init(){
        //有点区间DP的意思
        for(int j=0; (1<<(j+1))<n; j++){
            for(int i=1; i<=n; i++){
                if(fa[i][j] < 0) fa[i][j+1] = -1;
                else fa[i][j+1] = fa[fa[i][j]][j];
            }
        }
    }
    //给定俩个结点在线求其LCA
    int LCA(int u, int v){
        //保证v是较深的点
        if(depth[u] > depth[v]) swap(u , v);
        int temp = depth[v] - depth[u];//深度差
        //先把v调到与u等高处
        for(int i=0; (1<<i)<=temp; i++){
            if((1<<i) & temp) v = fa[v][i];
        }
        if(u==v) return u;
        //然后两个人比翼双飞
        for(int i=(int)(log(1.0*n)/log(2.0)); i >= 0; i--){
            if(fa[u][i] != fa[v][i]){
                u = fa[u][i];
                v = fa[v][i];
            }
        }
        return fa[u][0];
    
    }
    int main(){
        sc(t);
        int u,v;
        while(t--){
            sc(n);
            rep(i , 1, n){
                G[i].clear();
                in[i] = 0;
            }
            rep(i , 1, n-1){
                sc2(u,v);
                G[u].push_back(v);
                G[v].push_back(u);
                in[v]++;
            }
            sc2(s,e);
            int root;
            rep(i , 1, n) if(in[i] == 0) root = i; //找到树根节点
            dfs(root, -1, 0);
            init();
            pfd(LCA(s,e));
            
        }
        return 0;
    }
    
    
    • RMQ
    
    //RMQ 把LCA转化为求区间深度最值
    
    #include<iostream>
    #include<queue>
    #include<list>
    #include<vector>
    #include<cstring>
    #include<set>
    #include<stack>
    #include<map>
    #include<cmath>
    #include<algorithm>
    #include<string>
    #include<stdio.h>
    using namespace std;
    typedef long long ll;
    #define MS(x,i) memset(x,i,sizeof(x))
    #define rep(i,s,e) for(int i=s; i<=e; i++)
    #define sc(a) scanf("%d",&a)
    #define scl(a) scanf("%lld",&a)
    #define sc2(a,b) scanf("%d %d", &a, &b)
    #define debug printf("debug......
    ");
    #define pfd(x) printf("%d
    ",x)
    #define pfl(x) printf("%lld
    ",x)
    const double eps=1e-8;
    const double PI = acos(-1.0);
    const int inf = 0x3f3f3f3f;
    const ll INF = 0x7fffffff;
    const int maxn = 1e4+10;
    int dx[4] = {0, 0, 1, -1};
    int dy[4]  = {1, -1, 0 , 0};
    int n;
    vector<int> G[maxn];//存树
    int s,e;
    int depth[2*maxn];///存结点深度
    int order[2*maxn];//存储树的dfs序列
    int dp[2*maxn][20];//存以i为起点的长度为2^j的区间最大值在dfs序列所处的位置
    int first[maxn];//存储结点i第一次被访问到时在dfs序列中的位置
    int cnt;//记数  dfs序列
    bool vis[maxn];//访问标记
    int in[maxn];
    void init(){
        rep(i,1,n){
            G[i].clear();
            vis[i] = 0;
            in[i] = 0;
        }
        cnt = 0;
    }
    void dfs(int u, int d){
        vis[u] = 1;
        order[++cnt] = u;
        depth[cnt] = d;
        first[u] = cnt;
        for(int i=0; i<G[u].size(); i++){
            int v = G[u][i];
            if(!vis[v]){
                dfs(v,d+1);
                order[++cnt] = u;
                depth[cnt] = d;
            }
        }
    }
    
    void ST(int n){
        for(int i=1; i<=n; i++) dp[i][0] = i;
        int k = (int)(log2(n*1.0));
        for(int j=1; j<=k; j++)
        for(int i=1; i+(1<<j)-1<=n; i++){
            int a = dp[i][j-1];
            int b = dp[i+(1<<(j-1))][j-1];
            if(depth[a] < depth[b]) dp[i][j] = a;
            else dp[i][j] = b;
        }
    }
    
    int RMQ(int l , int r){
        int k = (int)(log2(1.0*r - l + 1));
        int a = dp[l][k];
        int b = dp[r - (1<<k) + 1][k];
        if(depth[a] < depth[b]) return a;
        else return b;
    
    }
    
    int LCA(int x, int y){
        //先找出x ,y第一次出现的位置
        int l = first[x];
        int r = first[y];
        if(l > r) swap(l , r);//注意左边小于右边
        //在[l,r]内寻找深度最大的位置
        int pos = RMQ(l , r);
        return order[pos];
    }
    int t;
    int main(){
        sc(t);
        int u,v;
        while(t--){
            sc(n);
            init();
            rep(i , 1, n-1){
                sc2(u,v);
                G[u].push_back(v);
                //G[v].push_back(u);
                in[v]++;
            }
            sc2(s,e);
            int root;
            rep(i , 1, n) if(in[i] == 0) root = i; //找到树根节点
            dfs(root , 1);
            ST(2*n-1);
            pfd(LCA(s,e));
            
        }
        return 0;
    }
    
  • 相关阅读:
    antd Upload的使用
    table 的使用方法
    标题前点的制作
    插件multiBtnList的使用
    render的写法
    数据请求
    实体类为什么使用包装类
    el-table中如何遍历数组中对象里的数组?
    关于hash的描述,hashcode etc
    Java集合框架详解
  • 原文地址:https://www.cnblogs.com/czsharecode/p/10765660.html
Copyright © 2011-2022 走看看