ZOJ 3195 Design the city(LCA 树上三点最短距离)

Design the city

Time Limit: 1000 msMemory Limit: 32768 KB

Cerror is the mayor of city HangZhou. As you may know, the traffic system of this city is so terrible, that there are traffic jams everywhere. Now, Cerror finds out that the main reason of them is the poor design of the roads distribution, and he want to change this situation.

In order to achieve this project, he divide the city up to N regions which can be viewed as separate points. He thinks that the best design is the one that connect all region with shortest road, and he is asking you to check some of his designs.

Now, he gives you an acyclic graph representing his road design, you need to find out the shortest path to connect some group of three regions.

Input

The input contains multiple test cases! In each case, the first line contian a interger N (1 < N < 50000), indicating the number of regions, which are indexed from 0 to N-1. In each of the following N-1 lines, there are three interger Ai, Bi, Li (1 < Li < 100) indicating there's a road with length Li between region Ai and region Bi. Then an interger Q (1 < Q < 70000), the number of group of regions you need to check. Then in each of the following Q lines, there are three interger Xi, Yi, Zi, indicating the indices of the three regions to be checked.

Process to the end of file.

Output

Q lines for each test case. In each line output an interger indicating the minimum length of path to connect the three regions.

Output a blank line between each test cases.

Sample Input

4
0 1 1
0 2 1
0 3 1
2
1 2 3
0 1 2
5
0 1 1
0 2 1
1 3 1
1 4 1
2
0 1 2
1 0 3

Sample Output

3
2

2
2

Author: HE, Zhuobin

题意:

 n 个点,n - 1条边,给出 m 次询问,询问连通三点的最短距离

思路:

a, b, c三点距离即(a, b的距离 + a, c的距离 + b, c的距离) /  2,举个栗子就明白了

tarjin实现:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int N = 5e4 + 10;
const int M = 7e4 + 10;

int n, m, dis[N], head[N], qu[M], tot, fa[N];
bool vis[N];

struct node
{
    int to, w, next;
}edge[N << 1];

struct query
{
    int u, v, lca, next;
}q[6 * M];

void init()
{
    tot = 0;
    memset(vis, 0, sizeof(vis));
    memset(head, -1, sizeof(head));
    memset(qu, -1, sizeof(qu));

}

void add(int u, int v, int w)
{
    edge[tot].to = v;
    edge[tot].next = head[u];
    edge[tot].w = w;
    head[u] = tot++;
}

void add2(int u, int v)
{
    q[tot].u = u;
    q[tot].v = v;
    q[tot].next = qu[u];
    qu[u] = tot++;
}

int Find(int x)
{
    if(fa[x] != x)
        fa[x] = Find(fa[x]);
    return fa[x];
}

void tarjin(int u)
{
    fa[u] = u;
    vis[u] = 1;
    for(int i = head[u]; ~i; i = edge[i].next)
    {
        int v = edge[i].to;
        if(!vis[v])
        {
            dis[v] = dis[u] + edge[i].w;
            tarjin(v);
            fa[v] = u;
        }
    }
    for(int i = qu[u]; ~i; i = q[i].next)
    {
        int v = q[i].v;
        if(vis[v])
        {
            q[i].lca = q[i ^ 1].lca = Find(v);
        }
    }
}

int main()
{
    int kcase = 0;
    while(~scanf("%d", &n))
    {
        init();
        int u, v, w;
        for(int i = 1; i < n; ++i)
        {
            scanf("%d%d%d", &u, &v, &w);
            add(u, v, w);
            add(v, u, w);
        }
        tot = 0;
        int a, b, c;
        scanf("%d", &m);
        for(int i = 1; i <= m; ++i)
        {
            scanf("%d%d%d", &a, &b, &c);
            add2(a, b);
            add2(b, a);
            add2(a, c);
            add2(c, a);
            add2(b, c);
            add2(c, b);
        }
        tarjin(0);
        if(kcase)
            cout<<'\n';
        kcase++;
        for(int i = 0; i < tot; i += 6)
        {
            int ans = 0, u, v, lca;
            u = q[i].u;
            v = q[i].v;
            lca = q[i].lca;
            ans += dis[u] + dis[v] - 2 * dis[lca];
            u = q[i + 2].u;
            v = q[i + 2].v;
            lca = q[i + 2].lca;
            ans += dis[u] + dis[v] - 2 * dis[lca];
            u = q[i + 4].u;
            v = q[i + 4].v;
            lca = q[i + 4].lca;
            ans += dis[u] + dis[v] - 2 * dis[lca];
            cout << ans / 2 << '\n';
        }
    }
    return 0;
}

树上倍增实现

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int N = 5e4 + 10;

//dis[i]:点i到根节点的距离
//fa[i][j]:节点i的第2^j个父亲,每个点最多2^(logN)个父亲,所以第二维开logN
//dep[i]:当前节点的深度
int n, m, dis[N], head[N], fa[N][21], tot, dep[N];
struct Edge
{
    int to, next, w;
}edge[N << 1];

void init()
{
    tot = 0;
    memset(dis, 0, sizeof(dis));
    memset(head, -1, sizeof(head));
    memset(fa, 0, sizeof(fa));
}

void add(int u, int v, int w)
{
    edge[tot].next = head[u];
    edge[tot].to = v;
    edge[tot].w = w;
    head[u] = tot++;
}

void dfs(int u, int father)   //当前节点和它的父亲
{
    dep[u] = dep[father] + 1;
    for(int i = 1; (1 << i) <= dep[u]; ++i)
    {
        if(fa[u][i - 1])
            fa[u][i] = fa[fa[u][i - 1]][i - 1];
        else break;   //如果该点没有第2^(i - 1)个父亲了,也不会有更远的父亲
    }
    for(int i = head[u]; ~i; i = edge[i].next)  //遍历相邻的所有点
    {
        int v = edge[i].to;
        if(v != father) // v 不是 u 的父亲,就是 u 的儿子
        {
            dis[v] = dis[u] + edge[i].w;    //更新距离
            fa[v][0] = u;   // v 的第2^0个父亲即第一个父亲是 u
            dfs(v, u);
        }
    }
}

int lca(int u, int v)
{
    if(dep[u] < dep[v]) //默认 u 比 v 深
        swap(u, v);
    for(int i = 20; i >= 0; --i)    //从大到小枚举使 x 和 y 到达同一层
    {
        if(dep[fa[u][i]] >= dep[v])
            u = fa[u][i];
        if(u == v)
            return u;
    }
    for(int i = 20; i >= 0; --i)
    {
        if(fa[u][i] != fa[v][i])
        {
            u = fa[u][i];
            v = fa[v][i];
        }
    }
    return fa[u][0];
}

int main()
{
    int u, v, w, a, b, c, kcase = 0;
    while(~scanf("%d", &n))
    {
        init();
        for(int i = 1; i < n; ++i)
        {
            scanf("%d%d%d", &u, &v, &w);
            add(u, v, w);
            add(v, u, w);
        }
        dfs(0, 0);  //选取1为根节点,1的父亲是0
        if(kcase) cout<<'\n';
        kcase++;
        scanf("%d", &m);
        while(m--)
        {
            scanf("%d%d%d", &a, &b, &c);
            int ans = dis[a] + dis[b] - 2 * dis[lca(a, b)] + dis[a] + dis[c] - 2 * dis[lca(a, c)] + dis[b] + dis[c] - 2 * dis[lca(b, c)];
            ans /= 2;
            cout<<ans<<'\n';
        }
    }
    return 0;
}

 

全部评论

相关推荐

11-28 17:48
中山大学 C++
点赞 评论 收藏
分享
10-17 16:07
门头沟学院 Java
牛牛大你18号:在汇报,突然弹出来,,领导以为我在准备跳槽,刚从领导办公室谈心出来
点赞 评论 收藏
分享
Hello_WordN:咱就是说,除了生命其他都是小事,希望面试官平安,希望各位平时也多注意安全
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务