uva11354 - Bond 最小瓶颈路+LCA

Once again, James Bond is on his way tosaving the world. Bond's latest mission requires him to travel between severalpairs of cities in a certain country.

 

The country has N cities(numbered by 1, 2, . . ., N),connected by M bidirectionalroads. Bond is going to steal a vehicle, and drive along the roads from city s to city t. The country'spolice will be patrolling the roads, looking for Bond, however, not all roadsget the same degree of attention from the police.

 

More formally, for each road MI6has estimated its dangerousness, the higher it is, the more likely Bond isgoing to be caught while driving on this road. Dangerousness of a path from s to t is defined asthe maximum dangerousness of any road on this path.

 

Now, it's your job to help Bond succeedin saving the world by finding the least dangerous paths for his mission.

 

 

Input

There will be at most 5 cases in theinput file.

The first line of each case contains twointegers N, M (2 ≤ N≤ 50000,1≤ M ≤ 100000)– number of cities and roads. The next M lines describe the roads. The i-thof these lines contains three integers: xi, yi, di (1 ≤ xi, yiN, 0 ≤ di ≤ 109)- the numbers of the cities connected by the ith road and itsdangerousness.

 

Description of the roads is followed bya line containing an integer Q (1 ≤ Q ≤ 50000),followed by Q lines, the i-thof which contains two integers si and ti (1 ≤ si, ti  ≤ N, si != ti).

 

Consecutive input sets are separated bya blank line.

 

Output

For each case, output Q lines, the i-thof which contains the minimum dangerousness of a path between cities si and ti. Consecutiveoutput blocks are separated by a blank line.

 

The input filewill be such that there will always be at least one valid path.

 

Sample Input

Output for Sample Input

4 5

1 2 10

1 3 20

1 4 100

2 4 30

3 4 10

2

1 4

4 1

 

2 1

1 2 100

1

1 2

20

20

 

100


  最小瓶颈路是u-v的一条路径,使路径上的最长边尽量短。由kruskal可以知道最小生成树中u-v的路径就是最小瓶颈路。

  这题有Q个询问,每次询问两个点之间的最小瓶颈路。首先求出最小生成树,并改写成有根树,让f[a]和cost[i]分别表示i的父亲节点和它与父亲的边权,L[i]表示节点i的深度(根为0)。接着预处理计算数组anc和maxcost数组,anc[i][j]表示节点i的第2^j级祖先编号(-1表示不存在),maxcost[i][j]表示节点i和它2^j级祖先之间路径上的最大权值。这两个数组的计算方法是一层一层递推,类似于RMQ的方法,时间复杂度O(logn)。处理完后查询p,q(设L[p]>=L[q]),先把p提升到和q同一层,如果这时p和q相等说明q就是他们的公共祖先,否则再把p和q同时提升直到fa[p]=fa[q],也就是p和q的父亲是他们的公共祖先,在提升p,q的这个过程中根据maxcost更新答案。因为p和q都是找最大的2的幂提升的,所以时间复杂度是O(logn)。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<set>
#include<utility>
#include<cstring>
#include<stack>
#include<queue>
#include<map>
#include<deque>
#include<cmath>
#include<map>
#define INF 0x3f3f3f3f
#define MAXN 100010
#define LOGMAXN 20
#define eps 1e-3
using namespace std;
typedef long long LL;
int N,M,Q,pa[MAXN],fa[MAXN],cost[MAXN];
vector<int> G[MAXN],C[MAXN];
struct Edge{
    int x,y,dist;
    bool operator < (const Edge& rhs) const{
        return dist<rhs.dist;
    }
}e[MAXN];
struct LCA{
    int n;
    int fa[MAXN];   //父亲节点
    int cost[MAXN]; //和父亲的费用
    int L[MAXN];    //层次,根节点层次为0
    int anc[MAXN][LOGMAXN];     //anc[p][i]是结点p的第2^i级父亲。anc[i][0] = fa[i]
    int maxcost[MAXN][LOGMAXN]; //maxcost[p][i]是i和anc[p][i]的路径上的最大费用

    //预处理,根据fa和cost数组求出anc和maxcost数组
    void preprocess(){
        for(int i=0;i<n;i++){
            anc[i][0]=fa[i];
            maxcost[i][0]=cost[i];
            for(int j=1;(1<<j)<n;j++) anc[i][j]=-1;
        }
        for(int j=1;(1<<j)<n;j++)
            for(int i=0;i<n;i++) if(anc[i][j-1]!=-1){
                int a=anc[i][j-1];
                anc[i][j]=anc[a][j-1];
                maxcost[i][j]=max(maxcost[i][j-1],maxcost[a][j-1]);
            }
    }
    int query(int p,int q){
        if(L[p]<L[q]) swap(p,q);
        int log;
        for(log=1;(1<<log)<=L[p];log++);
        log--;
        int ret=-INF;
        for(int i=log;i>=0;i--) if(L[p]-(1<<i)>=L[q]){
            ret=max(ret,maxcost[p][i]);
            p=anc[p][i];
        }
        if(p==q) return ret;    //LCA为p
        for(int i=log;i>=0;i--) if(anc[p][i]!=-1&&anc[p][i]!=anc[q][i]){
            ret=max(ret,maxcost[p][i]);
            p=anc[p][i];
            ret=max(ret,maxcost[q][i]);
            q=anc[q][i];
        }
        ret=max(ret,cost[p]);
        ret=max(ret,cost[q]);
        return ret; //LCA为fa[p](它也等于fa[q])
    }
}solver;

int find(int x){
    return x==pa[x]?x:pa[x]=find(pa[x]);
}

//最小生成树
void MST(){
    int cnt=0;
    sort(e,e+M);
    for(int i=0;i<N;i++){
        pa[i]=i;
        G[i].clear();
        C[i].clear();
    }
    for(int i=0;i<M;i++){
        int x=e[i].x,y=e[i].y,d=e[i].dist,u=find(x),v=find(y);
        if(u!=v){
            pa[u]=v;
            G[x].push_back(y);
            C[x].push_back(d);
            G[y].push_back(x);
            C[y].push_back(d);
            if(++cnt==N-1) break;
        }
    }
}

//无根树转化成有根树,记录每个节点的父节点和这个节点的层次
void DFS(int u,int fa,int level){
    solver.L[u]=level;
    int len=G[u].size();
    for(int i=0;i<len;i++){
        int v=G[u][i];
        if(v!=fa){
            solver.fa[v]=u;
            solver.cost[v]=C[u][i];
            DFS(v,u,level+1);
        }
    }
}

int main(){
    freopen("in.txt","r",stdin);
    int cas=0;
    while(scanf("%d%d",&N,&M)!=EOF){
        if(cas++) puts("");
        int x,y,dist;
        for(int i=0;i<M;i++){
            scanf("%d%d%d",&x,&y,&dist);
            e[i]=(Edge){x-1,y-1,dist};
        }
        MST();
        solver.n=N;
        memset(fa,-1,sizeof(fa));
        DFS(0,-1,0);
        solver.preprocess();
        int p,q;
        scanf("%d",&Q);
        while(Q--){
            scanf("%d%d",&p,&q);
            printf("%d\n",solver.query(p-1,q-1));
        }
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值