• LCA——笔记


    题目——最近公共祖先

    Tagjan

    将所有的查询存起来

    然后一遍dfs,得出所有LCA

    非常奇妙

    主要依据于以下操作
    13 和 14 的 LCA 是7

    当dfs到7时 (color{pink}{模拟}) 断开 3 - 7

    然后搜左子树

    vis[13] = 1
    

    再搜右子树时

    搜到14 并从此继续搜索 回溯后检查发现vis[13] = 1 update答案 
    

    也可以说

    最近公共LCA以下 两结点不在同一子树

    
    #include <queue>
    #include <vector>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
     
    inline void Read(int &x)
    {
        x = 0;
        char a = getchar();
        bool f = 0;
        while(a < '0'||a > '9') {if(a == '-') f = 1;a = getchar();}
        while(a >= '0'&&a <= '9') {x = x * 10 + a - '0';a = getchar();}
        if(f) x *= -1;
    }
    const int MAXN = 500001;
    vector<int> G[MAXN];
    vector<pair<int,int> > Ask[500001];
    //存询问
    int f[MAXN],ans[MAXN];
    /*
    f用并查集来维护加删边
    ans用于统一输出
    */
    bool vis[MAXN],Get[MAXN];
    inline int Find_set(int x)
    {
        if(f[x] == x) return x;
        return f[x] = Find_set(f[x]);
    }
    inline void Union(int u,int v)
    {
    	int A1 = Find_set(u),A2 = Find_set(v);
    	if(A1 != A2) f[A1] = A2;
    }
    //并查集的标准操作
    inline void Tagjan(int x)
    {
        int i;
        for(i = 0;i < G[x].size();i++)
        {
            if(!vis[G[x][i]])
            {
                vis[G[x][i]] = 1;
                Tagjan(G[x][i]);
                Union(G[x][i],x);
                /*先Tagjan下去,再连边
            	因为要模拟断边
            	*/
            }
        }
        //遍历整个子树
        for(i = 0;i < Ask[x].size();i++)
        {
            int v = Ask[x][i].first,Index = Ask[x][i].second;
            if(vis[v]) ans[Index] = Find_set(v);
        	/*因为u已经遍历到了
        	v在其他子树的话,上层断边,确保ans
        	v就在此子树的话,u已断边,v必搜到u
        	*/
        }
    }
    int main()
    {
        int i,n,m,s;
        Read(n),Read(m),Read(s);
        for(i = 1;i < n;i++)
        {
        	f[i] = i;
        	int u,v;
        	Read(u),Read(v);
            G[u].push_back(v);
            G[v].push_back(u);
        }
        for(i = 1;i <= m;i++)
        {
        	int u,v;
        	Read(u),Read(v);
        	Ask[u].push_back(make_pair(v,i));
        	Ask[v].push_back(make_pair(u,i));
        	//两边都要push进去,因为不清楚谁更浅
        }
        vis[s] = 1;
        //初始一定要清!!!!!
    	Tagjan(s);
        for(i = 1;i <= m;i++)
            printf("%d
    ",ans[i]);
        return 0;
    }
    

    Another Way

    线段树维护,又依据于以下原则

    有一种叫dfs序的东西 把上图的4,3,7,5看成一棵树(to be convenient

    dfs序为3437353

    那任两结点的公共祖先就在上序的两对应值间

    如7和4 ->3

    但这还不足以用线段树来维护

    就新增数组

    dep[i] 记录i的深度
    

    就变成了一个静态区间查询问题

    显然 还可用其它数据结构

    这里洛谷的过了点这

    然而没有吸氧 只是有个点差两毫秒TLE啊

    #include <queue>
    #include <vector>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
     
    inline void Read(int &x)
    {
        x = 0;
        char a = getchar();
        bool f = 0;
        while(a < '0'||a > '9') {if(a == '-') f = 1;a = getchar();}
        while(a >= '0'&&a <= '9') {x = x * 10 + a - '0';a = getchar();}
        if(f) x *= -1;
    }
    const int MAXN = 500001;
    int tot,seq[MAXN << 1],Index[MAXN],dep[MAXN + 2];
    //seq[]记录dfs序,Index记录结点在dfs序的位子,dep深度(说过了
    vector<int> G[MAXN];
    bool vis[MAXN];
    inline void dfs(int x)
    {
        seq[++tot] = x;
        Index[x] = tot;
        for(int i = 0;i < G[x].size();i++)
        {
            if(!vis[G[x][i]])
            {
                vis[G[x][i]] = 1;
                dep[G[x][i]] = dep[x] + 1;
                dfs(G[x][i]);
                seq[++tot] = x;
            }
        }
    }
    template<typename T>
    inline T Min(T a,T b) {if(a < b) return a;return b;}
    template<typename T>
    inline void exchange(T &a,T &b) {T c = a;a = b;b = c;}
    int tree[MAXN << 3],Left,Right;
    //tree[MAXN << 3]一定是3,因为是针对seq[MAXN << 1]的
    inline void tree_build(int l,int r,int k)
    {
        if(l == r)
        {
            tree[k] = seq[l];
            return;
        }
        int mid = l + r >> 1;
        tree_build(l,mid,k << 1);
        tree_build(mid + 1,r,k << 1 ^ 1);
        if(dep[tree[k << 1]] < dep[tree[k << 1 ^ 1]])
            tree[k] = tree[k << 1];
        else tree[k] = tree[k << 1 ^ 1];
        //是比较深度,不是比较编号大小
    }
    inline int query(int l,int r,int k)
    {
        if(Left <= l&&r <= Right)
            return tree[k];
        int mid = l + r >> 1,A1,A2;
        A1 = A2 = MAXN + 1;
        //main中写了dep[MAXN + 1] = 极大值
        if(Left <= mid) 
            A1 = query(l,mid,k << 1);
        if(mid < Right)
            A2 = query(mid + 1,r,k << 1 ^ 1);
        if(dep[A1] < dep[A2])
            return A1;
        return A2;
    }
    int main()
    {
        dep[MAXN + 1] = 5000011;
        int n,m,i,S;
        Read(n),Read(m);
        Read(S);
        //它给定根
        for(i = 1;i <= n - 1;i++)
        {
            int u,v,w;
            Read(u),Read(v);
            G[u].push_back(v);
            G[v].push_back(u);
        }
        vis[S] = 1;
        dfs(S);
        tree_build(1,tot,1);
        while(m--)
        {
            int u,v;
            Read(u),Read(v);
            Left = Index[u],Right = Index[v];
            if(Left > Right) exchange(Left,Right);
            //可能顺序不对
            int LCA = query(1,tot,1);
            printf("%d
    ",LCA);
        }
        return 0;
    }
    

    延伸

    如果求 u 到 v 的最短距离

    可以变成

    power[u] + power[v] - 2 * power[LCA(u,v)]
    

    倍增

    #include <vector>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
     
    inline void Read(int &x)
    {
        x = 0;
        char a = getchar();
        bool f = 0;
        while(a < '0'||a > '9') {if(a == '-') f = 1;a = getchar();}
        while(a >= '0'&&a <= '9') {x = x * 10 + a - '0';a = getchar();}
        if(f) x *= -1;
    }
    const int MAXN = 50001,MAXK = 50;
    int dep[MAXN],f[MAXN][MAXK];
    long long power[MAXN],all;
    bool vis[MAXN];
    vector<int> G[MAXN],Road[MAXN];
    inline void dfs(int u)
    {
    	for(int i = 0;i < G[u].size();i++)
    	{
    		if(!vis[G[u][i]])
    		{
    			vis[G[u][i]] = 1;
    			dep[G[u][i]] = dep[u] + 1;
    			f[G[u][i]][0] = u;
    			power[G[u][i]] = power[u] + Road[u][i];
    			dfs(G[u][i]);
    		}
    	}
    }
    inline void adjust(int &u,int val)
    {
    	for(int i = MAXK - 1;i >= 0;i--)
    		if(dep[f[u][i]] >= val)
    			u = f[u][i];
    }
    inline int lca(int u,int v)
    {
    	if(dep[u] > dep[v]) adjust(u,dep[v]);
    	else if(dep[u] < dep[v]) adjust(v,dep[u]);
    	if(u == v) return u;
    	for(int i = MAXK - 1;i >= 0;i--)
    		if(f[u][i] != f[v][i])
    			u = f[u][i],v = f[v][i];
    	return f[u][0];
    }
    inline void count(int u,int v)
    {
    	int w = lca(u,v);
    	all += power[u] + power[v] - power[w] * 2;
    }
    int main()
    {
    	int n;
    	bool F = 0;
        while(~scanf("%d",&n))
        {
        	if(F) putchar('
    ');
    		F = 1;
    		int i,m;
        	memset(vis,0,sizeof(vis));
        	memset(dep,0,sizeof(dep));
        	memset(f,0,sizeof(f));
        	memset(power,0,sizeof(power));
        	for(i = 1;i <= n;i++) G[i].clear(),Road[i].clear();
        	for(i = 1;i < n;i++)
        	{
        		int u,v,w;
        		Read(u),Read(v),Read(w);
        		u++,v++;
        		G[u].push_back(v);
        		G[v].push_back(u);
        		Road[u].push_back(w);
        		Road[v].push_back(w);
    		}
    		vis[1] = 1;
    		dfs(1);
    		f[1][0] = 1;
    		for(i = 1;i < MAXK;i++)
    			for(int j = 1;j <= n;j++)
    				f[j][i] = f[f[j][i - 1]][i - 1];
    		Read(m);
    		while(m--)
    		{
    			int a,b,c;
    			Read(a),Read(b),Read(c);
    			a++,b++,c++;
    			all = 0;
    			count(a,b),count(b,c),count(a,c);
    			printf("%lld
    ",all / 2);
    	 	}
    	 	
    	}
        return 0;
    }
    
  • 相关阅读:
    IDL打包发布exe(包含ENVI环境)
    电磁辐射基本概念
    ENVI_REGISTER_DOIT( )函数
    serverlet Web.xml配置代码解释
    Java设置session超时(失效)的时间
    JSP处理XML
    初学JSP
    顶部固顶导航菜单代码css+js
    oracle 关于删除指令的(我每天拼了命的努力,就是为了向那些看不起我的人证明,你们是对的)
    oracle题
  • 原文地址:https://www.cnblogs.com/resftlmuttmotw/p/11323277.html
Copyright © 2020-2023  润新知