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

 

全部评论

相关推荐

想干测开的tomca...:让我来压力你!!!: 这份简历看着“技术词堆得满”,实则是“虚胖没干货”,槽点一抓一大把: 1. **项目描述是“技术名词报菜名”,没半分自己的实际价值** 不管是IntelliDoc还是人人探店,全是堆Redis、Elasticsearch、RAG这些时髦词,但你到底干了啥?“基于Redis Bitmap管理分片”是你写了核心逻辑还是只调用了API?“QPS提升至1500”是你独立压测优化的,还是团队成果你蹭着写?全程没“我负责XX模块”“解决了XX具体问题”,纯把技术文档里的术语扒下来凑字数,看着像“知道名词但没实际动手”的实习生抄的。 2. **短项目塞满超纲技术点,可信度直接***** IntelliDoc就干了5个月,又是RAG又是大模型流式响应又是RBAC权限,这堆活儿正经团队分工干都得小半年,你一个后端开发5个月能吃透这么多?明显是把能想到的技术全往里面塞,生怕别人知道你实际只做了个文件上传——这种“技术堆砌式造假”,面试官一眼就能看出水分。 3. **技能栏是“模糊词混子集合”,没半点硬核度** “熟悉HashMap底层”“了解JVM内存模型”——“熟悉”是能手写扩容逻辑?“了解”是能排查GC问题?全是模棱两可的词,既没对应项目里的实践,也没体现深度,等于白写;项目里用了Elasticsearch的KNN检索,技能栏里提都没提具体掌握程度,明显是“用过但不懂”的硬凑。 4. **教育背景和自我评价全是“无效信息垃圾”** GPA前10%这么好的牌,只列“Java程序设计”这种基础课,分布式、微服务这些后端核心课提都不提,白瞎了专业优势;自我评价那堆“积极认真、细心负责”,是从招聘网站抄的模板吧?没有任何和项目挂钩的具体事例,比如“解决过XX bug”“优化过XX性能”,纯废话,看完等于没看。 总结:这简历是“技术名词缝合怪+自我感动式凑数”,看着像“背了后端技术栈名词的应届生”,实则没干货、没重点、没可信度——面试官扫30秒就会丢一边,因为连“你能干嘛”都没说清楚。
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

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