【noip2013】货车运输 (最大生成树 + 倍增)

 

题目描述 Description

 

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

 

输入描述 Input Description

 

第一行有两个用一个空格隔开的整数 n,m,表示 A 国有 n 座城市和 m 条道路。
接下来 m 行每行 3 个整数 x、y、z,每两个整数之间用一个空格隔开,表示从 x 号城市到 y 号城市有一条限重为 z 的道路。注意:x 不等于 y,两座城市之间可能有多条道路。
接下来一行有一个整数 q,表示有 q 辆货车需要运货。
接下来 q 行,每行两个整数 x、y,之间用一个空格隔开,表示一辆货车需要从 x 城市运输货物到 y 城市,注意:x 不等于 y。

 

输出描述 Output Description

 

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

 

样例输入 Sample Input

 

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

 

样例输出 Sample Output

 

3
-1
3

 

数据范围及提示 Data Size & Hint

 

对于 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。

题目分析

首先若两地不联通直接输出-1(并查集判断)。要想限重大,那么一定是在最大生成树上走,剩下的工作就是找出再生成树上x-y的路径上的最小权值,直接枚举边肯定会TLE,考虑使用倍增算法:fa[u][i]表示u向上跳$2^i$后的点是谁,这样fa[u][0]就是父节点。minn[u][i]表示u到fa[u][i]这条路径上的最小权值。dfs时就可以预处理出来,接着就可以像求lca一样求出去最小权值。

code

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;

const int N = 1e4 + 5, M = 5e4 + 5, oo = 0x3f3f3f3f;
int n, m, ecnt, Q;
struct Edge{
    int u, v, w;
}edge[M << 1];
int adj[N], go[M << 1], nxt[M << 1], len[M << 1];
int anc[N], Log[N];
int fa[N][30], minn[N][30], dep[N];
bool vst[N];

inline bool cmp(const Edge &a, const Edge &b){
    return a.w>b.w;
}

inline void addEdge(int u, int v, int w){
    nxt[++ecnt] = adj[u], adj[u] = ecnt, go[ecnt] = v, len[ecnt] = w;
    nxt[++ecnt] = adj[v], adj[v] = ecnt, go[ecnt] = u, len[ecnt] = w;
}

inline void dfs(int u){
    for(int i = 1; fa[u][i - 1]; i++){
        fa[u][i] = fa[fa[u][i - 1]][i - 1];
        minn[u][i] = min(minn[u][i - 1], minn[fa[u][i - 1]][i - 1]);
    }
    for(int e = adj[u], v; e; e = nxt[e]){
        if((v = go[e]) == fa[u][0]) continue;
        fa[v][0] = u;
        dep[v] = dep[u] + 1;
        minn[v][0] = len[e];
        dfs(v);
        vst[v] = true;
    }
}

inline int query(int u, int v){
    if(dep[u] < dep[v]) swap(u, v);
    int delta = dep[u] - dep[v], ret = oo;
    for(int i = Log[delta]; i >= 0; i--)
        if((1 << i) & delta){
            ret = min(ret, minn[u][i]);
            u = fa[u][i];
        }
    if(u == v) return ret;
    for(int i = Log[dep[u]]; i >= 0; i--)
        if(fa[u][i] != fa[v][i]){
            ret = min(ret, min(minn[u][i], minn[v][i]));
            u = fa[u][i], v = fa[v][i];
        }
    ret = min(ret, min(minn[u][0], minn[v][0]));
    return ret;
}

inline void initLog(){
    Log[0] = -1;
    for(int i = 1; i <= n; i++)
        Log[i] = Log[i >> 1] + 1; 
}

inline int getAnc(int u){
    return anc[u] == u ? u : (anc[u] = getAnc(anc[u]));
}

inline int read(){
    int i = 0, f = 1; char ch = getchar();
    for(; (ch < '0' || ch > '9') && ch != '-'; ch = getchar());
    if(ch == '-') f = -1, ch = getchar();
    for(; ch >= '0' && ch <= '9'; ch = getchar())
        i = (i << 3) + (i << 1) + (ch - '0');
    return i * f;
}

inline void wr(int x){
    if(x < 0) putchar('-'), x = -x;
    if(x > 9) wr(x / 10);
    putchar(x % 10 + '0');
}

int main(){
    n = read(), m = read();
    initLog();
    for(int i = 1; i <= n; i++) anc[i] = i;
    for(int i = 1; i <= m; i++){
        int x = read(), y = read(), z = read();
        edge[i].u = x, edge[i].v = y, edge[i].w = z;
    }
    sort(edge + 1, edge + m + 1, cmp);
    for(int i = 1; i <= m; i++){
        Edge e = edge[i];
        int f = getAnc(e.u), g = getAnc(e.v);
        if(f != g){
            anc[f] = g; 
            addEdge(e.u, e.v, e.w);
        }
    }
    for(int i = 1; i <= n; i++)
        if(!vst[i]){
            vst[i] = true;
            dfs(i);
        }
    Q = read();
    while(Q--){
        int x = read(), y = read();
        int f = getAnc(x), g = getAnc(y);
        if(f != g)
            wr(-1), putchar('\n');
        else wr(query(x, y)), putchar('\n');
    }
    return 0;
}
posted @ 2017-08-17 23:24  CzYoL  阅读(210)  评论(0编辑  收藏  举报