CF #600 Div.2 F. Cheap Robot//最短路+最小生成树+lca

题目

题意

给有权无向图,有k个中心点,油耗为距离,每次到中心点可以加油,给q个询问:从中心a走到中心b,如果每次加油数相同,每次最少加多少油保证可以从a到b。

思路

可以把中心点连超级源点,然后跑最短路,在最短路树上枚举每一条边构建新图(只含中心点的图)。
然后可以在新图上跑最小生成树,构建新新图(因为只要保证经过的边权最大值最小)
然后就是在新新图上进行q个询问,那么lca暴力搞就可以。
其实可以不用lca,带秩并查集可以解决(我不会)

/*
   Author: Rshs
   Time:   2019-11-17-22.06
*/
#include<bits/stdc++.h>
using namespace std;
#define  FI    first
#define  SE    second
#define  LL    long long
#define  MP    make_pair
#define  PII   pair<int,int>
#define  SZ(a) (int)a.size()
const double pai = acos(-1);
const double eps = 1e-8;
const LL     mod = 1e9+7;
const int    MXN = 2e5;
const int    MXLOG = 20;
vector<pair<LL,int> >g[MXN],G[MXN];
LL d[MXN];
int dfg[MXN],sour[MXN];
int fa[MXN];
int found(int x) {if(x==fa[x])return x;return fa[x]=found(fa[x]);}
int bei[MXN][MXLOG],de[MXN];
LL val[MXN][MXLOG];
void dfs(int u,int p){//所有都是向上走2^k步 所代表的含义
    for(auto i:G[u]) {
        if(i.SE!=p){
            de[i.SE]=de[u]+1;bei[i.SE][0]=u;val[i.SE][0]=i.FI;
            dfs(i.SE,u);
        }
    }
}
void initLCA(int nn){
    bei[1][0]=-1;de[1]=0;dfs(1,-1);
    for(int k=0;k<MXLOG-1;k++){  //-1!!!
        for(int v=1;v<=nn;v++){
            if(bei[v][k]<0)bei[v][k+1]=-1;
            else bei[v][k+1]=bei[bei[v][k]][k],val[v][k+1]=max(val[v][k],val[bei[v][k]][k]);
        }
    }
}
LL query(int u,int v){
    if(de[u]>de[v]) swap(u,v);
    LL re=-1;
    for(int k=0;k<MXLOG;k++){
        if((de[v]-de[u])>>k&1)
            re=max(re,val[v][k]),v=bei[v][k];
    }
    if(u==v) return re;
    for(int k=MXLOG-1;k>=0;k--){
        if(bei[u][k]!=bei[v][k]){
            re=max(re,val[v][k]),re=max(re,val[u][k]);
            u=bei[u][k],v=bei[v][k];
        }
    }
    re=max(re,val[u][0]);re=max(re,val[v][0]);
    return re;
}
int main(){
    int n,m,k,q;cin>>n>>m>>k>>q;
    for(int i=1;i<=m;i++){
        int su,sv,sw;scanf("%d%d%d",&su,&sv,&sw);
        g[su].push_back(MP(sw,sv));g[sv].push_back(MP(sw,su));
    }
    priority_queue<pair<LL,int> > pq;
    for(int i=1;i<=n;i++) d[i]=LLONG_MAX/2;
    for(int i=1;i<=k;i++) pq.push(MP(0,i)),d[i]=0,sour[i]=i;
    while(!pq.empty()){
        auto now=pq.top();pq.pop();
        int u=now.SE;
        if(dfg[u]) continue;
        dfg[u]=1;
        for(auto ed:g[u]){
            LL dis=ed.FI+d[u];
            if(dis>d[ed.SE]) continue;
            d[ed.SE]=dis;sour[ed.SE]=sour[u];
            pq.push(MP(-dis,ed.SE));
        }
    }
    vector< tuple<LL,int,int> >krs;
    for(int i=1;i<=n;i++) fa[i]=i;
    for(int i=1;i<=n;i++){
        for(auto ed:g[i]){
            if(sour[i]!=sour[ed.SE]) krs.push_back(make_tuple(ed.FI+d[i]+d[ed.SE],sour[i],sour[ed.SE]));
        }
    }
    sort(krs.begin(),krs.end());
    for(auto i:krs){
        int xx=found(get<1>(i));
        int yy=found(get<2>(i));
        if(xx==yy) continue;
        fa[xx]=fa[yy];
        G[get<1>(i)].push_back(MP(get<0>(i),get<2>(i)));
        G[get<2>(i)].push_back(MP(get<0>(i),get<1>(i)));
    }
    initLCA(n);
    while(q--){
         int sa,sb;scanf("%d%d",&sa,&sb);
         cout<<query(sa,sb)<<'\n';
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值