火车运输(NOIP2013)

传送药水
(喝了这一壶传送药水,保你看了什么题都觉得神清气爽)
这题不算水。
首先想到,肯定需要跑一遍最大生成树,因为我们需要尽可能大的限制。
那么,接下来就是求两点之间最小限制。
那么就是我们就可以用求LCA(最近公共祖先)来解决这个问题。
所以就倍增好了。
我们这里倍增的有两个数组,一个是祖先,另一个是当前点到祖先的路上的最小限制。
最后理一遍思路:
先kruskal建立一个最大树(图)。
然后bfs建立一个树,处理一些基本数据。
然后就用倍增求LCA来解决就好了。
代码如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
struct edge{
    int u,v,w;
};
int n,m;
int p[10005];
edge e[50005];
int f[10005][21],deep[10005];
int g[10005][21];
int vis[10005];
vector<int> G[10005],H[10005];
//int fa[10005];
int ques;
int find(int xx){
    return xx==p[xx]?xx:p[xx]=find(p[xx]);
}
int cmp(edge a,edge b){
    return a.w>b.w;
}
void kruscal(){
    sort(e,e+m,cmp);
    for(int i=1;i<=n;i++){
        p[i]=i;
    }
    for(int i=0;i<m;i++){
        int xx=find(e[i].u),yy=find(e[i].v);
        if(xx!=yy){
            p[xx]=yy;
            G[e[i].u].push_back(e[i].v);
            G[e[i].v].push_back(e[i].u);
            H[e[i].u].push_back(e[i].w);
            H[e[i].v].push_back(e[i].w);

        }
    }
}
void bfs(){
    memset(g,0x3f,sizeof(g));
    memset(vis,0,sizeof(vis));
    memset(f,-1,sizeof(f));
    queue<int> q;
    int cur=3;
    deep[cur]=1;
    q.push(cur);
    vis[cur]=1;
//  f[cur][0]=-1;
//  fa[cur]=-1;
    while(!q.empty()){
        cur=q.front();
        q.pop(); 
        int len=G[cur].size();
        for(int i=0;i<len;i++){
            if(vis[G[cur][i]]==0){
                q.push(G[cur][i]);
                vis[G[cur][i]]=1;
                deep[G[cur][i]]=deep[cur]+1;
                f[G[cur][i]][0]=cur;
//              fa[G[cur][i]]=cur;//待定 
                g[G[cur][i]][0]=H[cur][i];
            }
        }
    }
}
void init(){
    for(int k=0;k<20;k++){
        for(int j=1;j<=n;j++){
            if(f[j][k]!=-1){
                f[j][k+1]=f[f[j][k]][k];
                g[j][k+1]=min(g[j][k],g[f[j][k]][k]);
            }
        }
    }
}
int ans;
int jump(int u,int step){
    for(int k=0;k<21;k++){
        if(step&(1<<k)){
            ans=min(ans,g[u][k]);
            u=f[u][k];

        }
    }
    return u;
}
void lca(int u,int v){
    ans=0x3f3f3f3f;
    if(deep[u]<deep[v]){
        swap(u,v);
    }
    u=jump(u,deep[u]-deep[v]);
    for(int k=20;k>=0;k--){
        if(f[u][k]!=f[v][k]){
            ans=min(min(ans,g[u][k]),g[v][k]);
            u=f[u][k];
            v=f[v][k];
        }
    }
    ans=u==v?ans:min(min(ans,g[u][0]),g[v][0]);
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=0;i<m;i++){
        e[i].w=0;
    }
    for(int i=0;i<m;i++){
        int ww;
        scanf("%d%d%d",&e[i].u,&e[i].v,&ww);
        e[i].w=max(e[i].w,ww);
    }
    kruscal();
    bfs();
    init();
    scanf("%d",&ques);
    while(ques--){
        int u,v;
        scanf("%d%d",&u,&v);
        if(u>n||v>n){
            printf("-1\n");
            continue;
        }
        if(find(u)!=find(v)){
            printf("-1\n");
            continue;
        }else{
            lca(u,v);
            if(ans<0x3f3f3f3f){
                printf("%d\n",ans);
            }else{
                printf("-1\n");
            }
        }
    }
    return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值