题解 P1967 【货车运输】

题目描述

A国有n座城市,编号从 1 到 n,城市之间有 m 条双向道路。每一条道路对车辆都有重量限制,简称限重。现在有 q 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。

输入输出格式

输入格式:

第一行有两个用一个空格隔开的整数 n,m,表示 A 国有 n 座城市和 m 条道路。

接下来 m 行每行3个整数 x, y, z,每两个整数之间用一个空格隔开,表示从 x号城市到 y号城市有一条限重为 z 的道路。注意: x 不等于 y,两座城市之间可能有多条道路 。

接下来一行有一个整数 q,表示有 q 辆货车需要运货。

接下来 q 行,每行两个整数 x、y,之间用一个空格隔开,表示一辆货车需要从 x 城市运输货物到 y 城市,注意: x 不等于 y 。

输出格式:

共有 q 行,每行一个整数,表示对于每一辆货车,它的最大载重是多少。如果货车不能到达目的地,输出−1。

输入输出样例

输入样例#1:

4 3
1 2 4
2 3 3
3 1 1
3
1 3
1 4
1 3

输出样例#1: 复制

3
-1
3

说明

对于 30%的数据,0 < n < 1,000,0 < m < 10,000,0 < q< 1,000;

对于 60%的数据,0 < n < 1,000,0 < m < 50,000,0 < q< 1,000;

对于 100%的数据,0 < n < 10,000,0 < m < 50,000,0 < q< 30,000,0 ≤ z ≤ 100,000。

主要思路: Kruskal重构树 + LCA

这个题的问题是求一棵树,使得两点之间的简单路径中最小边权最大

这是一个Kruskal重构树的一个基础问题,就是快速求得最小生成树上的边权最值问题。

这里是重构一棵树,像之前一样跑Kruskal,只是在每次选边时把选上的边都新建一个节点,这个节点的点权值是这个边的边权,然后把这两个点与新建节点连边,每次连边同时用并查集把新建节点一起维护。

当连完边后,并不是说直接dfs建树,而是要讨论一个问题:可能图不连通,所以这时可能是一个森林,所以每个联通块都要建树,如果不在一个联通块里,直接“-1”。

code:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <ctime>
#include <algorithm>
#include <queue>
#include <map>
#include <vector>
using namespace std;
#define go(i, j, n, k) for(int i = j; i <= n; i += k)
#define fo(i, j, n, k) for(int i = j; i >= n; i -= k)
#define rep(i, x) for(int i = h[x]; i; i = e[i].nxt)
#define mn 200010
#define inf 1 << 30	
#define ll long long 
inline int read() {
    int x = 0, f = 1; char ch = getchar();
    while(ch > '9' || ch < '0') { if(ch == '-') f = -f; ch = getchar(); } 
    while(ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;	
}
// 记得开2倍数组啊,因为Kruskal重构树有最多2n - 1个节点
// 此处光荣的采用树剖LCA(逃
struct kr{
    int x, y, w;
} ee[mn << 1];
inline bool cmp(kr a, kr b) {
    return a.w > b.w;
}
struct edge{
    int v, nxt;
} e[mn << 1];
int h[mn], p, w[mn];
inline void add(int a, int b) {
    e[++p].nxt = h[a], h[a] = p, e[p].v = b;
}
inline void aadd(int i, int a, int b, int c) {
    ee[i].x = a, ee[i].y = b, ee[i].w = c;
//	cout << a << " " << b << " " << c << "\n";
}
// edge and node --------------------
int n, m, father[mn << 1], tot, cnt, q, vis[mn];
inline int findx(int x) {
    if(x == father[x]) return x;
    else return father[x] = findx(father[x]);
}
// union-find set -------------------
int fa[mn], sze[mn], top[mn], son[mn], dep[mn];
void dfs1(int x, int f, int deep) {
    fa[x] = f;
    sze[x] = 1;
    dep[x] = deep;
    vis[x] = 1;
    int maxson = -1;
    rep(i, x) {
        int v = e[i].v;
        if(v == f) continue;
        dfs1(v, x, deep + 1);
        sze[x] += sze[v];
        if(sze[v] > maxson)
            maxson = sze[v], son[x] = v;
    }
}
void dfs2(int x, int topf) {
    top[x] = topf;
    if(!son[x]) return;
    dfs2(son[x], topf);
    rep(i, x) {
        int v = e[i].v;
        if(v == fa[x] || v == son[x]) continue;
        dfs2(v, v);
    }
}
inline int LCA(int x, int y) {
    while(top[x] != top[y]) {
        if(dep[top[x]] < dep[top[y]]) swap(x, y);
        x = fa[top[x]];
    }
    return dep[x] < dep[y] ? x : y;
}	
// LCA ---------------------------------
inline void Kru() { // 基本差不多的Kruskal
    tot = n;
    go(i, 1, n, 1) father[i] = i;
    sort(ee + 1, ee + m + 1, cmp);
    go(i, 1, m, 1) {
        int xx = findx(ee[i].x);
        int yy = findx(ee[i].y);
        if(xx != yy) {
            tot++;
			// 毕竟是新建节点嘛QwQ
            father[tot] = father[xx] = father[yy] = tot;
            add(xx, tot);
            add(tot, xx);
            add(yy, tot);
            add(tot, yy);
			// 记得连无向边
            w[tot] = ee[i].w;
            if(++cnt == n - 1) 
                break;
        }
    }
    go(i, 1, tot, 1) { // 森林
        if(!vis[i]) {
            int f = findx(i);
            dfs1(f, 0, 1), dfs2(f, f);
        }
    }
}
// Kruskal Refactoring Tree ----------------------
int ff[mn];
inline int findf(int x) {
    if(x == ff[x]) return ff[x];
    else return ff[x] = findf(ff[x]);
}
int used[mn];
int main() {
    n = read(), m = read();
    go(i, 1, n, 1) ff[i] = i;
    go(i, 1, m, 1) {
        int a = read(), b = read(), c = read();
        aadd(i, a, b, c);
        int aa = findf(a), bb = findf(b);
        if(rand() % 2) ff[aa] = bb; // 随机合并大发吼啊
        else ff[bb] = aa;
    }
    Kru(); 
    q = read();
    go(i, 1, q, 1) {
        int x = read(), y = read();
        int xx = findx(x), yy = findx(y);
        if(xx != yy) {
            printf("-1\n");
            continue;
        } else {
            printf("%d\n", w[LCA(x, y)]);
        }
    }
    return 0;
}
posted @ 2018-12-02 23:41  yizimi  阅读(195)  评论(0编辑  收藏  举报