首页 > 代码库 > Gym - 100676H Capital City(边强连通分量 + 树的直径)

Gym - 100676H Capital City(边强连通分量 + 树的直径)

H. Capital City
[ Color: Black ]
Bahosain has become the president of Byteland, he is doing his best to make people‘s lives
easier. Now, he is working on improving road networks between the cities.
If two cities are strongly connected, people can use BFS (Bahosain‘s Fast Service) to travel
between them in no time. Otherwise, they have to follow one of the shortest paths between
them, and of course, they will use BFS when they can!
Two cities are connected if there is a path between them, and they are strongly connected if
after removing any single road they will remain connected.
President Bahosain wants to minimize the maximum distance people have to travel from any city
to reach the capital city, can you help him in choosing the capital city?


Input
The first line of input contains one integer T, the number of test cases (1 ≤ T ≤ 64).
The first line of each test case contains two integers n, m (1 ≤ n ≤ 100,000) (0 ≤ m ≤ 200,000), the
number of cities and the number of roads, respectively.
Each of the following m lines contains three space-separated integers a, b, c (1 ≤ a, b ≤ n) (1 ≤ c ≤
100,000), meaning that there is a road of length c connecting the cities a and b.
Byteland cities are connected since Bahosain became the president.
Test cases are separated with a blank line.


Output
For each test case, print the number of the city and length of the maximum shortest path on a
single line. If there is more than one possible city, print the one with the minimum number.


Sample Input
1
7 7
1 2 5
1 7 5
3 2 5
1 3 5
3 4 3
6 4 1
4 5 3

Sample Output
1 6

题意:

  在有个王国中,所有的城市都是间接或直接连通的,然后对于两个城市之间的路程,如果这两个城市是强连通的话,他们之间的路程可以看成0,否则的话就是路径的距离,强连通的话就是删除任何的一条路,这两个城市也是连通的,那么他们就是强连通的(Two cities are connected if there is a path between them, and they are strongly connected if after removing any single road they will remain connected.),现在这个王国要定首都,问首都定在哪个城市,能够让离首都最远的城市最近,输出首都的城市的编号和离首都最远的城市的距离,如果有多个的话城市编号最小。

思路:

  题目说得很明显,肯定是要边双连通分量缩点,将所有的边双连通分量缩点之后就会形成一颗树,所以问题就转化成了在一颗树上找一个点、使树上离这个点的最远的距离最短,然后考虑树的直径,为什么呢,假设当前点是树的直径上面的点,那么没有任何点的距离到该点的距离大于该点到直径两段的最大距离要大;然后假设当前点不是直径上面的点,那么当前点连到直径上的节点要比当前点更优

技术分享

代码:

/** @xigua */
#include<cstdio>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<vector>
#include<stack>
#include<cstring>
#include<queue>
#include<set>
#include<string>
#include<map>
#include<climits>
#define PI acos(-1)
using namespace std;
typedef long long ll;
typedef double db;
const int maxn = 1e5 + 5;
const int mod = 1e9 + 7;
const int INF = 1e8 + 5;
const ll inf = 1e15 + 5;
const db eps = 1e-9;

struct Edge {
    ll u, v, c;
} e[maxn<<2];
struct Ed {
    ll v, c;
};
int n, m, low[maxn], pre[maxn], tim, ebcc_cnt, du[maxn];
ll k, len, dis[maxn][2];
vector<int> G[maxn];
vector<Ed> ed[maxn];
int isbri[maxn<<4];
bool vis[maxn];

void init() {
    ebcc_cnt = tim = 0;
    for (int i = 1; i <= n; i++) G[i].clear();
    memset(isbri, 0, sizeof(isbri));
    memset(pre, 0, sizeof(pre));
    memset(du, 0, sizeof(du));
}

void tarjan(int u, int fa) {
    low[u] = pre[u] = ++tim;
    for (int i = 0; i < G[u].size(); i++) {
        int tmp = G[u][i];
        int v = e[tmp].v;
        if (!pre[v]) {
            tarjan(v, u);
            low[u] = min(low[u], low[v]);
            if (low[v] > pre[u]) // 子节点的反向边大于当前节点
                isbri[tmp] = isbri[tmp^1] = true; //标记为桥
        }
        else if (fa != v) // fa很重要 对于桥
            low[u] = min(low[u], pre[v]);
    }
}

void dfs(int u) {
    pre[u] = ebcc_cnt;
    for (int i = 0; i < G[u].size(); i++) {
        int tmp = G[u][i];
        if (isbri[tmp]) continue;
        int v = e[tmp].v;
        if (pre[v]) continue;
        dfs(v);
    }
}

void find_ebcc() {
    tarjan(1, -1);
    memset(pre, 0, sizeof(pre));
    for (int i = 1; i <= n; i++) {
        if (!pre[i]) {
            ebcc_cnt++;
            dfs(i);
        }
    }
}

void BFS(int s, int ca) {
    memset(vis, 0, sizeof(vis));
    queue<Ed> q;
    q.push((Ed){s, 0});
    vis[s] = 1;
    while (q.size()) {
        Ed tmp = q.front(); q.pop();
        dis[tmp.v][ca] = tmp.c;
        for (int i = 0; i < ed[tmp.v].size(); i++) {
            Ed xx = ed[tmp.v][i];
            if (!vis[xx.v]) {
                vis[xx.v] = 1;
                q.push((Ed){xx.v, xx.c + tmp.c});
            }
        }
    }
}

void dfs_len(int x, int fa, ll dep) {   //找直径
    if (dep > len) {
        k = x;
        len = dep;
    }
    for (int i = 0; i < ed[x].size(); i++) {
        Ed tmp = ed[x][i];
        if (tmp.v == fa) continue;
        dfs_len(tmp.v, x, dep + tmp.c);
    }
}

void solve() {
    cin >> n >> m;
    init();
    for (int i = 1; i <= m; i++) {
        int u, v, c; scanf("%d%d%d", &u, &v, &c);
        e[i<<1|1].u = u, e[i<<1|1].v = v, e[i<<1|1].c = c;
        e[i<<1].u = v, e[i<<1].v = u, e[i<<1].c = c;
        G[u].push_back(i<<1|1);
        G[v].push_back(i<<1);
    }
    find_ebcc();
    int tot = m<<1|1;
    for (int i = 1; i <= ebcc_cnt; i++) ed[i].clear();
    for (int i = 1; i <= tot; i += 2) {
        if (isbri[i]) {
            int u = e[i].v, v = e[i].u;
            ed[pre[u]].push_back((Ed){pre[v], e[i].c});
            ed[pre[v]].push_back((Ed){pre[u], e[i].c});
        }
    }
    len = -1;
    dfs_len(1, -1, 0);
    int st = k; len = -1;
    dfs_len(st, -1, 0);
    BFS(st, 0); //直径的两个端点
    BFS(k, 1);
    ll inx = n + 1, dd = inf;
    for (int i = 1; i <= n; i++) {
        int pr = pre[i];
        if (dis[pr][0] + dis[pr][1] != len) continue;   //判断是否是直径上的点
        ll tmp = max(dis[pr][0], dis[pr][1]);
        if (tmp < dd) {
            inx = i;
            dd = tmp;
        }
    }
    cout << inx << ‘ ‘ << dd << endl;
}

int main() {
    //cin.sync_with_stdio(false);
    //freopen("tt.txt", "r", stdin);
    //freopen("hh.txt", "w", stdout);
    int t = 1; cin >> t;

    while (t--) {
        solve();
    }
    return 0;
}
/*
2
7 7
1 2 5
1 7 5
3 2 5
1 3 5
3 4 3
6 4 1
4 5 3
3 3
1 2 3
1 3 3
2 3 3
*/

  

Gym - 100676H Capital City(边强连通分量 + 树的直径)