货车运输(最大生成树+树上倍增)

题目描述

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

输入

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

输出

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

样例输入

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

样例输出

3 3 3
− 1 -1 1
3 3 3

提示

对于 30 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 0<n<1,0000<m<10,0000<q<1,000
对于 60 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 0<n<1,0000<m<50,0000<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 0<n<10,0000<m<50,0000<q<30,0000z100,000

解析

题意即为求任意两点之间的一条路径使得该路径上边权最小的边最大。
这让我们想到了最大生成树的性质。
其实只要我们维护出了一棵最大生成树,并能快速求出树上两点间简单路径上的最小值就可以了。
树上倍增一下就好了。

代码

#include
#include
#include
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 = vet[e];
        if(v == fa) continue;
        f[v][0] = u;
        g[v][0] = val[e];
        dfs(v , u);
    }
}
inline int LCA(int u , int v)
{
    if(dep[u] < dep[v]) swap(u , v);
    int res = oo ;
    for(int i = 13;i >= 0;i--)
    {
        if(dep[f[u][i]] >= dep[v]) res = min(res , g[u][i]) , u = f[u][i];
        if(u == v) return res;
    }
    for(int i = 13;i >= 0;i--)
        if(f[u][i] != f[v][i])
            res = min(res , min(g[u][i] , g[v][i])) , u = f[u][i] , v = f[v][i];
    res = min(res , min(g[u][0] , g[v][0]));
    return res;
}
int main()
{
    int n = read() , m = read();
    clear_edge(n);
    for(int i = 1;i <= m;i++)
    {
        int u = read() , v = read() , cost = read();
        E[i] = (edge){u , v , cost};
        add_edge(u , v , cost);
        add_edge(v , u , cost);
    }
    for(int i = 1;i <= n;i++) vis[i] = 0;
    int num = 0;
    for(int i = 1;i <= n;i++)
        if(!vis[i]) Search(i) , rt[++num] = i;
    sort(E + 1 , E + m + 1 , cmp); 
    clear_edge(n);
    Kruskal(n , m , num);
    memset(g , 0x3f3f3f , sizeof g);
    for(int i = 1;i <= n;i++) dep[rt[i]] = 0 , dfs(rt[i] , 0);
    int q = read();
    while(q--)
    {
        int u = read() , v = read();
        if(ufs.Find(u) != ufs.Find(v)) puts("-1");
        else printf("%d\n",LCA(u , v));
    }
    return 0;
}

你可能感兴趣的:(树上倍增,最大生成树)