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;
}

 

全部评论

相关推荐

10-22 12:03
山东大学 Java
程序员小白条:26届一般都得有实习,项目可以随便写的,如果不是开源社区的项目,随便包装,技术栈也是一样,所以本质应该找学历厂,多投投央国企和银行,技术要求稍微低一点的,或者国企控股那种,纯互联网一般都得要干活
应届生简历当中,HR最关...
点赞 评论 收藏
分享
代码飞升_不回私信人...:别这样贬低自己,降低预期,放平心态,跟昨天的自己比。做好自己,反而会效率更高心态更好,加油兄弟
点赞 评论 收藏
分享
12-03 23:38
复旦大学 Java
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务