CF1253F Cheap Robot(神奇思路,图论,最短路,最小生成树/Kruskal 重构树/并查集)

神仙题。

先考虑平方级别的暴力怎么做。

明显答案有单调性,先二分 \(c\)

先最短路预处理 \(dis_u\) 表示 \(u\) 到离它最近的充电站的距离(一开始把 \(1\)\(k\) 全部丢到优先队列里就行了)。

考虑当前站在 \(u\) 点上时,剩余的电量是 \(x\)。注意到由于起点是充电站,就一定有 \(x\le c-dis_u\)(考虑最后一个走到的充电站沿最短路走到这)

如果 \(x<dis_u\),因为终点是充电站,肯定不可能再到终点。

否则就可以走到最近的充电站再回来,\(x\) 就可以变成 \(c-dis_u\)。前面也推过不可能变得更大。

于是就可以直接 DFS 了。


放个代码

void dfs(int u,ll x){
    if(x<0) return;
    vis1[u]=true;    //走到过这个点
    if(x<dis[u]) return;
    vis2[u]=true;    //走到过这个点并且电量大于dis[u]
    for(int i=head[u];i;i=nxt[i]){
        int v=to[i];
        if(vis2[v]) return;
        dfs(v,c-dis[u]-w[i]);
    }
}

怎么搞快点?

由于走到 \(u\) 时要 \(x\ge dis_u\) 才有用,所以考虑我们会走一条边 \((u,v,w)\),当且仅当 \(c-dis_u-w\ge dis_v\),即 \(dis_u+dis_v+w\le c\)

那么问题变成求一条从 \(a\)\(b\) 的路径使得路径上每条边的 \(dis_u+dis_v+w\) 的最大值最小(明显是满足条件的最小的 \(c\))。

还不会?

右转 NOIP2013 货车运输。

如果用最小生成树或者 Kruskal 重构树,时间复杂度大概是 \(O((n+m)\log n+m\log m+m\log n+q\log n)\)。(最短路+给边排序+求树+LCA)

(顺便提个并查集的做法:询问离线下来,森林中每棵树的根记录这里面有哪些点。使用按秩合并,每个点至多被合并 \(\log\) 次。)


Kruskal 重构树的代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int maxn=600060;
#define MP make_pair
#define PB push_back
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline ll read(){
    char ch=getchar();ll x=0,f=0;
    while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
    while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
    return f?-x:x;
}
struct node{
    ll d;
    int id;
    bool operator<(const node &nd)const{return d>nd.d;}
};
struct edge{
    int u,v;
    ll w;
    bool operator<(const edge &e)const{return w<e.w;}
}e[maxn];
int n,m,k,q,el,head[maxn],to[maxn],nxt[maxn],w[maxn],u_fa[maxn];
int cnt,el2,head2[maxn],to2[maxn],nxt2[maxn],fa[maxn],sz[maxn],son[maxn],top[maxn],dep[maxn];
ll wnd[maxn],dis[maxn];
priority_queue<node> pq;
inline void add(int u,int v,int w_){
    to[++el]=v;nxt[el]=head[u];head[u]=el;w[el]=w_;
}
inline void add2(int u,int v){
    to2[++el2]=v;nxt2[el2]=head2[u];head2[u]=el2;
}
int getfa(int x){
    return x==u_fa[x]?x:u_fa[x]=getfa(u_fa[x]);
}
void dfs1(int u,int f){
    sz[u]=1;
    dep[u]=dep[fa[u]=f]+1;
    for(int i=head2[u];i;i=nxt2[i]){
        int v=to2[i];
        if(v==f) continue;
        dfs1(v,u);
        sz[u]+=sz[v];
        if(sz[v]>sz[son[u]]) son[u]=v; 
    }
}
void dfs2(int u,int topf){
    top[u]=topf;
    if(son[u]) dfs2(son[u],topf);
    for(int i=head2[u];i;i=nxt2[i]){
        int v=to2[i];
        if(v==fa[u] || v==son[u]) continue;
        dfs2(v,v);
    }
}
int lca(int u,int v){
    while(top[u]!=top[v]){
        if(dep[top[u]]<dep[top[v]]) swap(u,v);
        u=fa[top[u]];
    }
    return dep[u]<dep[v]?u:v;
}
int main(){
    n=read();m=read();k=read();q=read();
    FOR(i,1,m){
        int u=read(),v=read(),w=read();
        add(u,v,w);add(v,u,w);
        e[i]=(edge){u,v,w};
    }
    MEM(dis,0x3f);
    FOR(i,1,k) pq.push((node){dis[i]=0,i});
    while(!pq.empty()){
        ll d=pq.top().d;
        int u=pq.top().id;
        pq.pop();
        if(d!=dis[u]) continue;
        for(int i=head[u];i;i=nxt[i]){
            int v=to[i];
            if(dis[v]>d+w[i]) pq.push((node){dis[v]=d+w[i],v});
        }
    }
    FOR(i,1,m) e[i].w+=dis[e[i].u]+dis[e[i].v];
    sort(e+1,e+m+1);
    FOR(i,1,2*n) u_fa[i]=i;
    cnt=n;
    FOR(i,1,m){
        int u=e[i].u,v=e[i].v;
        ll w=e[i].w;
        u=getfa(u);v=getfa(v);
        if(u==v) continue;
        u_fa[u]=u_fa[v]=++cnt;
        wnd[cnt]=w;
        add2(cnt,u);add2(cnt,v);
    }
    dfs1(cnt,0);dfs2(cnt,cnt); 
    while(q--){
        int u=read(),v=read();
        printf("%lld\n",wnd[lca(u,v)]);
    }
}

猜你喜欢

转载自www.cnblogs.com/1000Suns/p/11920509.html