[NOI2014]魔法森林——动态加点最短路spfa

lct维护动态加点MST点这里

题目大意:

给定一个图,每条边有两个权值,ai和bi,求一条路径使得这条路径上的边的 amax+bmax a m a x + b m a x 最小。

思路:

我们首先关注若只有一个权值的做法,其实就是一种最短路,用spfa解决即可,考虑到有两个权值,我们可以按照a的从小到大枚举,并且依次加边,每加一次边之后就可以跑一遍b值得spfa,若答案产生了更新,那么一定是新加的a产生了贡献,新的答案就是dis[n]+a了。
由于是不断地加边,若新加的边对答案产生影响,那么必然经过了这条边的两个端点,这里可以采用动态加点spfa,即每次新加进去一条边之后将这条边的两个端点放入队列中更新全图的答案。
又好打又快的算法!

/*==============================
 * Author : Ylsoi
 * Problem : [NOI2014]magic
 * File : [NOI2014]magic_spfa.cpp
 * Algorithm : spfa
 * Time : 2018.4.9
 * ============================*/
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
void File(){
    freopen("[NOI2014]magic_spfa.in","r",stdin);
    freopen("[NOI2014]magic_spfa.out","w",stdout);
}
#define REP(i,a,b) for(register int i=a;i<=b;++i)
#define DREP(i,a,b) for(register int i=a;i>=b;--i)
#define MREP(i,x) for(register int i=beg[x];i;i=E[i].last)
#define ll long long
#define inf (0x3f3f3f3f)
const int maxn=5e4+10;
const int maxm=1e5+10;
int n,m,beg[maxn],cnt,cnt1;
struct edge{int to,last,a,b;}E[maxm*2];
struct ee{
    int u,v,a,b;
    bool operator < (const ee &tt) const {
        return a<tt.a;
    }
}e[maxm];
void add(int u,int v,int a,int b){
    ++cnt;
    E[cnt].to=v;
    E[cnt].last=beg[u];
    beg[u]=cnt;
    E[cnt].a=a;
    E[cnt].b=b;
}
int Max(int _,int __){return _>__ ? _ : __;}
int Min(int _,int __){return _<__ ? _ : __;}
int dis[maxn],ans=inf;
bool vis[maxn];
queue<int>q;
void spfa(int aa){
    while(!q.empty()){
        int u=q.front();q.pop();
        vis[u]=0;
        MREP(i,u){
            int v=E[i].to;
            if(Max(dis[u],E[i].b)<dis[v]){
                dis[v]=Max(dis[u],E[i].b);
                if(!vis[v]){
                    vis[v]=1;
                    q.push(v);
                }
            }
        }
    }
    ans=Min(ans,aa+dis[n]);
}
int main(){
    File();
    scanf("%d%d",&n,&m);
    REP(i,1,m){
        int u,v,a,b;
        scanf("%d%d%d%d",&u,&v,&a,&b);
        ++cnt1;
        e[cnt1].u=u;e[cnt1].v=v;
        e[cnt1].a=a;e[cnt1].b=b;
    }
    sort(e+1,e+cnt1+1);
    REP(i,2,n)dis[i]=inf;
    int p=1;
    while(p<=m){
        int i=p;
        while(e[i].a==e[p].a){
            add(e[i].u,e[i].v,e[i].a,e[i].b);
            add(e[i].v,e[i].u,e[i].a,e[i].b);
            q.push(e[i].u);q.push(e[i].v);
            vis[e[i].u]=1;vis[e[i].v]=1;
            ++i;
        }
        spfa(e[p].a);
        p=i;
    }
    if(ans!=inf)printf("%d\n",ans);
    else printf("%d\n",-1);
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值