codeVS2370 Small computer room tree (tarjan LCA offline algorithm)

2370 Tree in a small computer room
Time limit: 1 s
Space limit: 256000 KB Problem
level: Diamond Problem
Solution Problem Description Description There is a tree in the small computer room. There are N nodes on the tree, and the node labels are from 0 to N-1. There are two bugs named Piaogou and Dajigou, living on two different nodes. One day, they want to crawl to a node to start a base, but as two bugs, they don't want to spend too much effort. Knowing that it takes c energy to climb from a node to its parent node (the same is true for climbing from the parent node to this node), they want to find a path that takes the shortest energy, so that the energy is strong when doing the base, they find You're asking you to design a program to find this way, asking you to tell them the minimum effort required

Input Description Input Description
The first line has an n, and the next n-1 lines have three integers u, v, c in each line. It means that it takes c effort for node u to climb to node v.
Line n+1 has an integer m indicating that there are m queries. In the next m lines, each line has two integers u, and v represents the node where the two bugs are located
. Output Description
There are m lines in total, each line has an integer, which represents the shortest distance obtained for this query.

Sample Input Sample Input
3

1 0 1

2 0 1

3

1 0

2 0

1 2

Sample Output
1

1

2

Data Range and Hint Data Size & Hint
1<=n<=50000, 1<=m<=75000, 0<=c<=1000

Problem solving ideas

·Let's talk about the tarjan LCA offline algorithm learned today .
For each point u:
1. Create a set of elements represented by u.
2. Traverse the node v connected to u. If it has not been visited, use the Tarjan-LCA algorithm for v. After the end, merge the set of v into the set of u.
3. For a query (u, v) related to u, if v has been visited, the result is the representative element of the set where v is located.

· For the above problem, we can first find the nearest common ancestor of the node where the two bugs are located. The weight of each edge can be stored by declaring a cost[] array. The depth of each point is stored in the depth[] array, and the answer is the depth of the two nodes (x, y) minus the depth of the nearest common ancestor (u) - depth[x]+depth[y]-2* depth[u]. In addition, this question uses chained forward stars to store the graph and all queries, head[] and edge[] for graphs, and qhead[] and qedge[] for queries. For each query, there are two in qedge, but only one of them can be answered. Of course, the other can be directly assigned when responding. Operations on sets can be done with union lookups.

·The following code and specific comments:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#define MAXN 1001009
using namespace std;
int head[MAXN * 2], qhead[MAXN * 2], vis[MAXN], f[MAXN], lca[MAXN * 2];
int tot, qtot, n, m, x[MAXN], y[MAXN], depth[MAXN], cost[MAXN * 2];
struct NODE
{
    int to;
    int next;
}edge[MAXN * 2], qedge[MAXN * 2];
void add(int a, int b, int c)//添加边的函数
{
    edge[++tot].next = head[a];
    edge[tot].to = b;
    head[a] = tot;
    cost[tot] = c;
}
void addq(int a, int b)//添加询问的函数
{
    qedge[++qtot].next = qhead[a];
    qedge[qtot].to = b;
    qhead[a] = qtot;
}
int find(int x)//寻找当前集合代表元素的函数(并查集的查找函数)
{
    return f[x] == x ? x : find(f[x]);
}
void lca_dfs(int x)//LCA搜索子树节点
{
    f[x] = x;
    vis[x] = 1;
    for (int i = head[x]; i; i = edge[i].next)
    {
        if (!vis[edge[i].to])//如果没被访问过
        {
            depth[edge[i].to] = depth[x] + cost[i];//记录当前深度
            lca_dfs(edge[i].to);
            f[edge[i].to] = x;//加入当前x集合
        }
    }
    for (int i = qhead[x]; i; i = qedge[i].next)//找到与当前x有询问关系的节点
    {
        if (vis[qedge[i].to])//如果这个节点被访问过
        {
            lca[i] = find(qedge[i].to);//lca[]记录当前询问的最近公共祖先的节点编号
            if (i % 2)
                lca[i + 1] = lca[i];
            else
                lca[i - 1] = lca[i];
        }
    }
}
int main()
{
    int u, v, c;
    cin >> n;
    for (int i = 1; i <= n - 1; i++)
    {
        cin >> u >> v >> c;
        add(u, v, c);
        add(v, u, c);//由于链式前向星只能储存有向边,也就是说存图时要储存(u,v)(v,u)两次
    }
    cin >> m;
    for (int i = 1; i <= m; i++)
    {
        cin >> x[i] >> y[i];
        addq(x[i], y[i]);
        addq(y[i], x[i]);//储存询问也要存两次
    }
    lca_dfs(1);//开始算法
    for (int i = 1; i <= m; i++)
    {
        cout << depth[x[i]] + depth[y[i]] - 2 * depth[lca[i * 2]] << endl;//答案就是询问的两个点(x,y)的深度和减去两倍最近公共祖先(u)的深度
    }
    return 0;
}

Reference books: ACM-ICPC programming series "Graph Theory and Application" (Harbin Institute of Technology Press)

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324818348&siteId=291194637