1. 程式人生 > >貨車運輸(最大生成樹+樹上倍增)

貨車運輸(最大生成樹+樹上倍增)

題目描述

A 國有 n n 座城市,編號從 1 1 n

n ,城市之間有 m m 條雙向道路。每一條道路對車輛都有重量限制,簡稱限重。現在有 q q
輛貨車在運輸貨物,司機們想知道每輛車在不超過車輛限重的情況下,最多能運多重的貨物。

輸入

輸入檔案第一行有兩個用一個空格隔開的整數 n n m m

,表示 A A 國有 n n 座城市和 m m 條道路。
接下來 m 行每行 3 個整數 x x y y z z ,每兩個整數之間用一個空格隔開,表示從 x x 號城市到 y y 號城市有一條限重為 z z 的道路。注意: x x 不等於 y y ,兩座城市之間可能有多條道路。接下來一行有一個整數 q q ,表示有 q q 輛貨車需要運貨。
接下來 q q 行,每行兩個整數 x x y y ,之間用一個空格隔開,表示一輛貨車需要從 x x 城市運輸貨物到 y y 城市,注意: x x 不等於 y y

輸出

輸出共有 q q 行,每行一個整數,表示對於每一輛貨車,它的最大載重是多少。如果貨車不能到達目的地,輸出 1 -1

樣例輸入

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

樣例輸出

3 3
1 -1
3 3

提示

對於 30 30 %的資料, 0 < n < 1 , 000 0 < m < 10 , 000 0 < q < 1 , 000 0 < n < 1,000,0 < m < 10,000,0 < q < 1,000
對於 60 60 %的資料, 0 < n < 1 , 000 0 < m < 50 , 000 0 < q < 1 , 000 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 0 < n < 10,000,0 < m < 50,000,0 < q < 30,000, 0 ≤ z ≤ 100,000

解析

題意即為求任意兩點之間的一條路徑使得該路徑上邊權最小的邊最大。
這讓我們想到了最大生成樹的性質。
其實只要我們維護出了一棵最大生成樹,並能快速求出樹上兩點間簡單路徑上的最小值就可以了。
樹上倍增一下就好了。

程式碼

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 10005;
const int maxe = 50005;
const int oo = 100000;
struct edge{int u , v , val;}E[maxe];
int edgenum;
int Next[maxe << 1] , vet[maxe << 1] , val[maxe << 1] , head[maxn];
bool vis[maxn];
int dep[maxn] , f[maxn][15] , g[maxn][15] , rt[maxn];
inline void swap(int &x , int &y){x ^= y , y ^= x , x ^= y;}
inline int min(int x , int y){return x < y ? x : y;}
struct UFS
{
    private:
        int fa[maxn];
    public:
        inline void init(int n){for(int i = 1;i <= n;i++) fa[i] = i;}
        int Find(int x){return fa[x] == x ? x : fa[x] = Find(fa[x]);}
        void Union(int x , int y){fa[Find(y)] = Find(x);}
}ufs;
inline int read()
{
    char ch = getchar();
    while(ch < '0' || ch > '9') ch = getchar();
    int res = 0;
    while(ch >= '0' && ch <= '9') res = (res << 3) + (res << 1) + (ch ^ 48) , ch = getchar();
    return res;
}
inline void clear_edge(int n)
{
    edgenum = 0;
    for(int i = 1;i <= n;i++) head[i] = 0;
}
inline void add_edge(int u , int v , int cost)
{
    edgenum++;
    Next[edgenum] = head[u];
    vet[edgenum] = v;
    val[edgenum] = cost;
    head[u] = edgenum;
}
void Search(int u)
{
    vis[u] = 1;
    for(int e = head[u];e;e = Next[e])
    {
        int v = vet[e];
        if(vis[v]) continue;
        Search(v);
    }
}
bool cmp(edge x , edge y){return x.val > y.val;}
inline void Kruskal(int n , int m , int num)
{
    ufs.init(n);
    int cnt = 0;
    for(int i = 1;i <= m;i++)
        if(ufs.Find(E[i].u) != ufs.Find(E[i].v))
        {
            ufs.Union(E[i].u , E[i].v);
            cnt++;
            add_edge(E[i].u , E[i].v , E[i].val);
            add_edge(E[i].v , E[i].u , E[i].val);
            if(cnt == n - num) break;
        }
}
void dfs(int u , int fa)
{
    dep[u] = dep[fa] + 1;
    for(int i = 1;i <= 13;i++)
    {
        f[u][i] = f[f[u][i - 1]][i - 1];
        g[u][i] = min(g[u][i - 1] , g[f[u][i - 1]][i - 1]);
    }
    for(int e = head[u];e;e = Next[e])
    {
        int v <