码迷,mamicode.com
首页 > 其他好文 > 详细

LCA统计

时间:2018-05-03 01:03:37      阅读:252      评论:0      收藏:0      [点我收藏+]

标签:include   font   scan   pen   字体   amp   变化   理解   log   

模板:HDU 2586

给一棵带权树 要求你求出从i到j的距离

离线:

①倍增 复杂度 O((n+q)logn) 或者 O(nlogn)

技术分享图片
/* Huyyt */
#include <bits/stdc++.h>
using namespace std;
const int maxn = 40001;  //点的最大值
const int maxl = 25; //深度的最大值
typedef struct
{
        int from, to, w;
} edge; //这个结构体用来存储边
vector<edge> edges;
vector<int> G[maxn];
//保存边的数组
int grand[maxn][maxl];  //x向上跳2^i次方的节点,x到他上面祖先2^i次方的距离
int gw[maxn][maxl];   //维护距离的数组
//int gwmax[maxn][maxl]; //维护边权最大值的数组
int depth[maxn];//深度
int root;
int n, m;
int N; //N的意思是最多能跳几层
void addedge(int x, int y, int w)  //把边保存起来的函数
{
        edge a = {x, y, w}, b = {y, x, w};
        edges.push_back(a);
        edges.push_back(b);
        G[x].push_back(edges.size() - 2);
        G[y].push_back(edges.size() - 1);
}
void dfs(int x)//dfs建图
{
        for (int i = 1; i <= N; i++) //第一个几点就全部都是0,第二个节点就有变化了,不理解的话建议复制代码输出下这些数组
        {
                grand[x][i] = grand[grand[x][i - 1]][i - 1];  //倍增 2^i=2^(i-1)+2^(i-1)
                gw[x][i] = gw[x][i - 1] + gw[grand[x][i - 1]][i - 1]; //维护一个距离数组
                //gwmax[x][i]=max(gwmax[x][i-1],gw[grand[x][i-1]][i-1]);
                // if(grand[x][i]==0) break;
        }
        for (int i = 0; i < G[x].size(); i++)
        {
                edge  e = edges[G[x][i]];
                if (e.to != grand[x][0]) //这里我们保存的是双向边所以与他相连的边不是他父亲就是他儿子父亲的话就不能执行,不然就死循环了。
                {
                        depth[e.to] = depth[x] + 1; //他儿子的深度等于他爸爸的加1
                        grand[e.to][0] = x; //与x相连那个节点的父亲等于x
                        gw[e.to][0] = e.w; //与x相连那个节点的距离等于这条边的距离
                        //gwmax[e.to][0]=e.w;
                        dfs(e.to);//深搜往下面建
                }
        }
}
void Init()
{
        //n为节点个数
        N = floor(log(n + 0.0) / log(2.0));//最多能跳的2^i祖先
        depth[root] = 0; //根结点的祖先不存在,用-1表示
        memset(grand, 0, sizeof(grand));
        memset(gw, 0, sizeof(gw));
        dfs(root);//以1为根节点建树
}
int lca(int a, int b)
{
        if (depth[a] > depth[b])
        {
                swap(a, b);        //保证a在b上面,便于计算
        }
        int ans = 0;
        for (int i = N; i >= 0; i--) //类似于二进制拆分,从大到小尝试
        {
                if (depth[a] < depth[b] && depth[grand[b][i]] >= depth[a]) //a在b下面且b向上跳后不会到a上面
                {
                        ans += gw[b][i], b = grand[b][i];        //先把深度较大的b往上跳
                }
        }
        for (int j = N; j >= 0; j--) //在同一高度了,他们一起向上跳,跳他们不相同节点,当全都跳完之后grand【a】【0】就是lca,上面有解释哈。
        {
                if (grand[a][j] != grand[b][j])
                {
                        ans += gw[a][j];
                        ans += gw[b][j];
                        a = grand[a][j];
                        b = grand[b][j];
                }
        }
        if (a != b) //a等于b的情况就是上面土色字体的那种情况
        {
                ans += gw[a][0], ans += gw[b][0];
        }
        return ans;
}
int main()
{
        int t ;
        scanf("%d", &t);
        while (t--)
        {
            root=1;
                scanf("%d%d", &n, &m);
                for (int i = 1; i < n; i++)
                {
                        int x, y, w;
                        scanf("%d%d%d", &x, &y, &w);
                        addedge(x, y, w);
                }
                Init();
                for (int i = 1; i <= m; i++)
                {
                        int x, y;
                        scanf("%d%d", &x, &y);
                        printf("%d\n", lca(x, y));
                }
        }
}
倍增版

②Tarjan复杂度 O(n+q)

技术分享图片
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cstdlib>
#include <cmath>
using namespace std;
const int N = 40000 + 5;
struct Edge
{
        int cnt, x[N], y[N], z[N], nxt[N], fst[N];
        void set()
        {
                cnt = 0;
                memset(x, 0, sizeof x);
                memset(y, 0, sizeof y);
                memset(z, 0, sizeof z);
                memset(nxt, 0, sizeof nxt);
                memset(fst, 0, sizeof fst);
        }
        void add(int a, int b, int c)
        {
                x[++cnt] = a;
                y[cnt] = b;
                z[cnt] = c;
                nxt[cnt] = fst[a];
                fst[a] = cnt;
        }
} e, q;
int T, n, m;
int root;
int from, to, dist;
int in[N],dis[N],fa[N],ans[N];
bool vis[N];
void dfs(int root)
{
        for (int i = e.fst[root]; i; i = e.nxt[i])
        {
                dis[e.y[i]] = dis[root] + e.z[i];
                dfs(e.y[i]);
        }
}
int getf(int k)
{
        return fa[k] == k ? k : fa[k] = getf(fa[k]);
}
void LCA(int root)
{
        for (int i = e.fst[root]; i; i = e.nxt[i])
        {
                LCA(e.y[i]);
                fa[getf(e.y[i])] = root;
        }
        vis[root] = 1;
        for (int i = q.fst[root]; i; i = q.nxt[i])
                if (vis[q.y[i]] && !ans[q.z[i]])
                {
                        ans[q.z[i]] = dis[q.y[i]] + dis[root] - 2 * dis[getf(q.y[i])];
                }
}
int main()
{
        scanf("%d", &T);
        while (T--)
        {
                q.set(), e.set();
                memset(in, 0, sizeof in);
                memset(vis, 0, sizeof vis);
                memset(ans, 0, sizeof ans);
                scanf("%d %d", &n, &m);
                for (int i = 1; i < n; i++)
                {
                        scanf("%d %d %d", &from, &to, &dist), e.add(from, to, dist), in[to]++;
                }
                for (int i = 1; i <= m; i++)
                {
                        scanf("%d %d", &from, &to), q.add(from, to, i), q.add(to, from, i);
                }
                root = 0;
                for (int i = 1; i <= n && root == 0; i++)
                {
                        if (in[i] == 0)
                        {
                                root = i;
                        }
                }
                dis[root] = 0;
                dfs(root);
                for (int i = 1; i <= n; i++)
                {
                        fa[i] = i;
                }
                LCA(root);
                for (int i = 1; i <= m; i++)
                {
                        printf("%d\n", ans[i]);
                }
        }
        return 0;
}
Tarjan版

 

LCA统计

标签:include   font   scan   pen   字体   amp   变化   理解   log   

原文地址:https://www.cnblogs.com/Aragaki/p/8983270.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!