• [LCA] 最近公共祖先


    最近公共祖先求法很多, 各有优略

    LCA步骤及原理:


     例题:

    http://acm.hdu.edu.cn/showproblem.php?pid=2586


    代码:

    LCA倍增法

    DFS + 向前星版
    预处理DEG 为log2(n) + 1
    #include <bits/stdc++.h>
    typedef long long ll;
    typedef long double ld;
    //typedef __int128 ill;
    const int MAXN = 1e6 + 10;
    const ld PI = acos(-1.0);
    const ld E = exp(1.0);
    using namespace std;
    
    int head[MAXN], cnt = 1, DEG;
    struct node
    {
        int to, next;
    }edge[MAXN];
    void insert(int x, int y)
    {
        edge[cnt].next = head[x];
        head[x] = cnt;
        edge[cnt++].to = y;
        edge[cnt].next = head[y];
        head[y] = cnt;
        edge[cnt++].to = x;
    }
    int deep[MAXN] = {0}, fa[MAXN][20] = {0};
    void dfs(int now, int pre)
    {
        deep[now] = deep[pre] + 1;
        fa[now][0] = pre;
        for(int i = 1; i <= DEG; ++i)
            fa[now][i] = fa[ fa[now][i - 1] ][i - 1];
        for(int p = head[now]; p; p = edge[p].next)
        {
            int x = edge[p].to;
            if(x != pre)
                dfs(x, now);
        }
    }
    int lca(int x, int y)
    {
        if(deep[x] > deep[y])
            swap(x, y);
        for(int i = DEG; i >= 0; --i)
        {
            if(deep[fa[y][i]] >= deep[x])
                y = fa[y][i];
        }
        
        if(x == y)
            return x;
        
        for(int i = DEG; i >= 0; --i)
        {
            if(fa[x][i] != fa[y][i])
                x = fa[x][i], y = fa[y][i];
        }
        
        return fa[x][0];
    }
    
    int main()
    {
        ios::sync_with_stdio(0);
        cin.tie(0); cout.tie(0);
        
        int N, M, S;
        
        cin >> N >> M >> S;
        
        DEG = log2(N) + 1;
        
        for(int i = 1, from, to; i < N; ++i)
        {
            cin >> from >> to;
            insert(from, to);
        }
        
        dfs(S, 0);
        
        while(M--)
        {
            int x, y;
            cin >> x >> y;
            cout << lca(x, y) << '
    ';
        }
        
        return 0;
    }
    
    /*
    3 1
    2 4
    5 1
    1 4
    */

     很复杂的一个版本

    typedef long long ll;
    typedef long double ld;
    typedef unsigned long long ull;
    const ld PI = acos(-1.0);
    const ld E = exp(1.0);
    const int INF = 0x3f3f3f3f;
    const int MAXN = 1e6 + 10;
    const int DEG = 20;
    const ll MOD = 1e9 + 7;
    
    vector < pair <int, int> > edge[MAXN];
    
    int deep[MAXN] = {0}, fa[MAXN][DEG]; //deep节点深度 fa[i][j]结点i的第2^j个爹
    ll sum[MAXN] = {0}; //本题用的前缀和
    
    int n, m;
    
    void reset(int n)
    {
    	for(int i = 1; i <= n; ++i)
    	{
    		edge[i].clear();
    	}
    	memset(deep, 0, sizeof(int) * (n + 10));
    	memset(fa, 0, sizeof(int) * (n + 10));
    	memset(sum, 0, sizeof(int) * (n + 10));
    	memset(used, 0, sizeof(int) * (n + 10));
    }
    
    
    void bfs(int root)
    {
    	queue <int> Q;
    	deep[root] = 0;
    	fa[root][0] = root;
    	Q.push(root);
    	while(!Q.empty())
    	{
    		int now = Q.front();
    		Q.pop();
    		for(int i = 1; i < DEG; ++i) //倍增处理爹
    			fa[now][i] = fa[fa[now][i - 1]][i - 1];
    
    		for(int i = 0; i < edge[now].size(); ++i)
    		{
    			int to = edge[now][i].first, val = edge[now][i].second;
    			if(to == fa[now][0]) continue; //处理过爹了跳过
    			deep[to] = deep[now] + 1; //记录深度
    			fa[to][0] = now; //记录爹
    			sum[to] = sum[now] + val; //本题记录的前缀和
    			Q.push(to);
    		}
    	}
    }
    
    int lca(int u, int v)
    {
    	if(deep[u] > deep[v]) //调整深度
    		swap(u, v);
    	int hu = deep[u], hv = deep[v];
    	int tu = u, tv = v;
    	for(int det = hv - hu, i = 0; det; det >>= 1, ++i)
    	{
    		if(det & 1)
    			tv = fa[tv][i];
    	}//跳至同一深度
    
    	if(tu == tv) return tu; //相同返回
    
    	for(int i = DEG - 1; i >= 0; --i) //继续跳至相同
    	{
    		if(fa[tu][i] == fa[tv][i])
    			continue;
    		tu = fa[tu][i];
    		tv = fa[tv][i];
    	}
    	return fa[tu][0];
    } 
    
    int main()
    {
        //ios::sync_with_stdio(false);
        //cin.tie(0);     cout.tie(0);
        //freopen("D://test.in", "r", stdin);
        //freopen("D://test.out", "w", stdout);
        
        int T;
    
        scanf("%d", &T);
    
        while(T--)
        {
        	scanf("%d%d", &n, &m);
    		
    		reset(n);
    		
        	int x, y, z;
    
        	for(int i = 1; i < n; ++i)
        	{
        		scanf("%d%d%d", &x, &y, &z);
        		edge[x].push_back(make_pair(y, z));
        		edge[y].push_back(make_pair(x, z));
        	}
    
        	bfs(1);
    
        	while(m--)
        	{
        		int a, b;
    
        		scanf("%d%d", &a, &b);
    
        		int d = lca(a, b);
        		
        		ll ans = sum[a] + sum[b] - 2 * sum[d];
    
        		printf("%lld
    ", ans);
        	}
        }
    
        return 0;
    }
    

    O(n)暴力查找

    typedef long long ll;
    typedef long double ld;
    typedef unsigned long long ull;
    const ld PI = acos(-1.0);
    const ld E = exp(1.0);
    const int INF = 0x3f3f3f3f;
    const int MAXN = 1e6 + 10;
    const ll MOD = 1e9 + 7;
    
    vector < pair <int, int> > edge[MAXN];
    
    int deep[MAXN] = {0}, fa[MAXN];
    bool used[MAXN] = {0};
    ll sum[MAXN] = {0};
    
    int n, m;
    
    void reset(int n)
    {
    	for(int i = 1; i <= n; ++i)
    	{
    		edge[i].clear();
    	}
    	memset(deep, 0, sizeof(int) * (n + 10));
    	memset(fa, 0, sizeof(int) * (n + 10));
    	memset(sum, 0, sizeof(int) * (n + 10));
    	memset(used, 0, sizeof(int) * (n + 10));
    }
    
    void dfs(int now)
    {
    	if(used[now])
    		return ;
    
    	used[now] = true;
    
    	for(int i = 0; i < edge[now].size(); ++i)
    	{
    		int to = edge[now][i].first, val = edge[now][i].second;
    
    		if(!used[to])
    		{
    			fa[to] = now;
    			deep[to] = deep[now] + 1;
    			sum[to] = sum[now] + val;
    			dfs(to);
    		}
    	}
    }
    
    int lca(int c, int d)
    {
    	while(deep[c] > deep[d])
    			c = fa[c];
    
    	while(deep[d] > deep[c])
    		d = fa[d];
    
    	while(d != c)
    		d = fa[d], c = fa[c];
    		
    	return d;
    } 
    
    int main()
    {
        //ios::sync_with_stdio(false);
        //cin.tie(0);     cout.tie(0);
        //freopen("D://test.in", "r", stdin);
        //freopen("D://test.out", "w", stdout);
        
        int T;
    
        scanf("%d", &T);
    
        while(T--)
        {
        	scanf("%d%d", &n, &m);
    		
    		reset(n);
    		
        	int x, y, z;
    
        	for(int i = 1; i < n; ++i)
        	{
        		scanf("%d%d%d", &x, &y, &z);
        		edge[x].push_back(make_pair(y, z));
        		edge[y].push_back(make_pair(x, z));
        	}
    
        	deep[1] = 1;
    
        	dfs(1);
    
        	while(m--)
        	{
        		int a, b;
    
        		scanf("%d%d", &a, &b);
        		
        		//cin >> a >> b;
    
        		int d = lca(a, b);
        		
        		ll ans = sum[a] + sum[b] - 2 * sum[d];
    
        		printf("%d
    ", ans);
        	}
        }
    
        return 0;
    }
  • 相关阅读:
    随机生成300道四则运算
    练习
    电梯演说模板练习
    敏捷开发
    团队模式
    思考
    build to win 观后感
    四则运算
    Code review
    Mutual review
  • 原文地址:https://www.cnblogs.com/zeolim/p/12270376.html
Copyright © 2020-2023  润新知