Codeforces 1253F - Cheap Robot(最短路+并查集/Kruskal树)

题目大意

n点m边无向图,k个关键点,q个询问(a,b)。问从a到b需要的最小容量c是多少。保证a,b是关键点。你能量池最多有c能量,每次走边消耗边权那么多的能量。在关键点会补满能量。每次询问需要的最少能量。
k , n ≤ 1 e 5 , q , m ≤ 3 e 5 k,n\le1e5,q,m\le 3e5 k,n1e5,q,m3e5

解题思路

以所有关键点为源求一遍多源最短路,然后对于一个点 u u u,如果当前能量 x < d i s [ u ] x < dis[u] x<dis[u],肯定到不了,如果 x > = d i s [ u ] x >= dis[u] x>=dis[u],那么 x x x应该是 ≤ c − d i s [ u ] \le c-dis[u] cdis[u]的。又因为 x > = d i s [ u ] x >= dis[u] x>=dis[u]所以可以跑到那个最近的关键点又回来,所以x每个点u的能量可以保持在 c − d i s [ u ] c-dis[u] cdis[u] u − > v u->v u>v可以走需要 c − d i s [ u ] − w > = d i s [ v ] c-dis[u]-w >= dis[v] cdis[u]w>=dis[v],转换成 d i s [ u ] + d i s [ v ] + w ≤ c dis[u]+dis[v]+w\le c dis[u]+dis[v]+wc。以 d i s [ u ] + d i s [ v ] + w dis[u]+dis[v]+w dis[u]+dis[v]+w为新边权建立新图。
问题就变成从一个点到另一个点经过的路径上的最大值最小可以是多少。
两种做法:
1.离线做法 把边按权值排序之后进行并查集。询问挂在点上。按需要处理的询问数量进行启发式合并,每个询问最多移动 l o g log log
2.在线做法 建立 Kruskal树之后进行树上倍增求lca的点权

离线做法:

#include
#define ll long long
#define lowbit(x) ((x)&(-(x)))
#define mid ((l+r)>>1)
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
#define P pair
using namespace std;
const int maxn = 3e5 + 50;
struct edge{
     
    int u ,v , nxt;
    ll w;
    bool operator < (const edge &a)const{
     return w < a.w;}
}e[maxn*2];
int cnt = 0;
int head[maxn];
void add(int u, int v, ll w){
     
    e[cnt] = (edge){
     u, v, head[u], w};
    head[u] = cnt++;
}
int n, m, k, Q;
priority_queue<P, vector<P>, greater<P> > q;
ll dis[maxn];
void dij(){
     
    while(q.size()){
     
        P temp = q.top();q.pop();
        int u = temp.second;
        if(dis[u] < temp.first) continue;
        for(int i = head[u]; ~i; i = e[i].nxt){
     
            int v = e[i].v; ll w = e[i].w;
            if(dis[v] > dis[u] + w){
     
                dis[v] = dis[u] + w;
                q.push(P(dis[v], v));
            }
        }
    }
}
void init(){
     
    memset(head, -1, sizeof head);
    cin>>n>>m>>k>>Q;
    for(int i = 0; i < m; ++i){
     
        int u, v; ll w; scanf("%d%d%lld", &u, &v, &w);
        add(u, v, w); add(v, u, w);
    }
    memset(dis, 0x3f, sizeof dis);
    for(int i = 1; i <= k; ++i){
     
        dis[i] = 0; q.push(P(0, i));
    }dij();
    int tp = 0;
    for(int i = 0; i < cnt; i += 2){
     
        e[tp] = e[i];
        e[tp++].w += dis[e[i].u]+dis[e[i].v];
    }
    cnt = tp;
}
ll ans[maxn*5];
struct node{
     int v, id;};
vector<node> g[maxn];
int fa[maxn];
int fnd(int x){
     
    if(x == fa[x]) return x;
    return fa[x] = fnd(fa[x]);
}
void link(int u, int v, ll w){
     
    u = fnd(u); v = fnd(v);
    if(u == v) return;
    if(g[u].size() > g[v].size()) swap(u, v);
    for(int i = 0; i < g[u].size(); ++i){
     
        int x = g[u][i].v;
        int id = g[u][i].id;
        if(fnd(x) == v)
            ans[id] = w;
        else
            g[v].push_back(g[u][i]);
    }
    fa[u] = v;
    g[u].clear();//g[u].shrink_to_fit(); return;
}
void sol(){
     
    for(int i = 1; i <= n; ++i) fa[i] = i;
    for(int i = 0; i < Q; ++i){
     
        int u, v;
        scanf("%d%d", &u, &v);
        g[u].push_back((node){
     v, i});
        g[v].push_back((node){
     u, i});
    }
    sort(e, e+cnt);
    for(int i = 0; i < cnt; ++i) link(e[i].u, e[i].v, e[i].w);
    for(int i = 0; i < Q; ++i) {
     
        printf("%lld\n", ans[i]);
    }
}
int main()
{
     
    init();
    sol();
}

在线做法:

#include
#define ll long long
#define lowbit(x) ((x)&(-(x)))
#define mid ((l+r)>>1)
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
#define P pair
using namespace std;
const int maxn = 3e5 + 50;
struct edge{
     
    int u ,v , nxt;
    ll w;
    bool operator < (const edge &a)const{
     return w < a.w;}
}e[maxn*2];
int cnt = 0;
int head[maxn];
void add(int u, int v, ll w){
     
    e[cnt] = (edge){
     u, v, head[u], w};
    head[u] = cnt++;
}
int n, m, k, Q;
priority_queue<P, vector<P>, greater<P> > q;
ll dis[maxn];
void dij(){
     
    while(q.size()){
     
        P temp = q.top();q.pop();
        int u = temp.second;
        if(dis[u] < temp.first) continue;
        for(int i = head[u]; ~i; i = e[i].nxt){
     
            int v = e[i].v; ll w = e[i].w;
            if(dis[v] > dis[u] + w){
     
                dis[v] = dis[u] + w;
                q.push(P(dis[v], v));
            }
        }
    }
}
void init(){
     
    memset(head, -1, sizeof head);
    cin>>n>>m>>k>>Q;
    for(int i = 0; i < m; ++i){
     
        int u, v; ll w; scanf("%d%d%lld", &u, &v, &w);
        add(u, v, w); add(v, u, w);
    }
    memset(dis, 0x3f, sizeof dis);
    for(int i = 1; i <= k; ++i){
     
        dis[i] = 0; q.push(P(0, i));
    }dij();
    int tp = 0;
    for(int i = 0; i < cnt; i += 2){
     
        e[tp] = e[i];
        e[tp++].w += dis[e[i].u]+dis[e[i].v];
    }
    cnt = tp;
}
ll ans[maxn];
int fa[maxn*2];
int fnd(int x){
     
    if(x == fa[x]) return x;
    return fa[x] = fnd(fa[x]);
}
int tot;
vector<int> g[maxn];
ll val[maxn];
int f[maxn][20], dep[maxn];
void get_mst(){
     
    tot = n;
    for(int i = 0; i <cnt; ++i){
     
        int u = e[i].u; int v = e[i].v;
        u = fnd(u); v = fnd(v); if(u == v) continue;
        fa[u] = fa[v] = ++tot; fa[tot] = tot; val[tot] = e[i].w;
        g[tot].push_back(v); f[v][0] = tot;
        g[tot].push_back(u); f[u][0] = tot;
    }
}
void dfs(int u){
     
    for(int i = 0; i+1 < 20; ++i) f[u][i+1] = f[f[u][i]][i];
    for(int i = 0; i < g[u].size(); ++i) dep[g[u][i]] =dep[u]+1, dfs(g[u][i]);
}
int lca(int u, int v){
     
    if(dep[u] > dep[v]) swap(u, v);
    int d = dep[v] - dep[u]; for(int i = 19; i >= 0; --i) if(d>>i&1) v = f[v][i];
    if(u == v) return u;
    for(int i = 19; i >= 0; --i){
     
        if(f[u][i] == f[v][i]) continue;
        u = f[u][i]; v = f[v][i];
    }return f[u][0];
}
void sol(){
     
    for(int i = 1; i <= n; ++i) fa[i] = i;
    sort(e, e+cnt);
    get_mst();
    dfs(tot);
    while(Q--){
     
        int u, v; scanf("%d%d", &u, &v);
        printf("%lld\n", val[lca(u,v)]);
    }
}
int main()
{
     
    init();
    sol();
}

你可能感兴趣的:(启发式算法,图论)