zoukankan      html  css  js  c++  java
  • 题解 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 << "
    ";
    }
    // 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
    ");
                continue;
            } else {
                printf("%d
    ", w[LCA(x, y)]);
            }
        }
        return 0;
    }
    
  • 相关阅读:
    【贴吧】计算器代码注释
    简单理解面向对象思维
    [转载]编程哲理
    Android SDK Manager无法更新的解决方案
    CSS选择器
    javascript arguments参数问题
    html垂直居中
    x64系统WSC注册方法
    SharePoint Server 2013安装
    Asp Url汉字乱码的问题
  • 原文地址:https://www.cnblogs.com/yizimi/p/10056336.html
Copyright © 2011-2022 走看看