H-トラベリングプランZOJ-4001(最小最大パス、最小スパンツリー)

BaoBaoは旅行が大好きです。彼の旅行マップには(N)の都市がマークされており、食料を持っている都市と持っていない都市があります。

彼の旅の間、彼は飢えないように十分な食物をとらなければなりません。地図上には(M)の道路があり、それぞれが2つの都市を接続しています。これらの道路は双方向です。道路ごとに、食料の正確なコストがわかります。バオバオが食料を提供できる都市に到着したときのみ、彼は食料を最大限に補充できることに注意してください。

BaoBaoには(Q)の旅程計画があります。計画ごとに、出発都市と目的都市の両方に食料が供給されていることが保証されています。今、BaoBaoは、各計画の馬車の最大食料容量を決定するのを手伝ってほしいと思っています。彼の便宜のために、彼はそれができるだけ小さくできることを望んでいます。

入力
テストケースは1つだけです。

入力の最初の行には、2つの整数(N)と(M)((1 \ le N \ le 10 ^ 5)、(1 \ le M \ le 2 \ times 10 ^ 5))が含まれており、( N)都市および(M)双方向道路。

2行目には、(N)個の整数(a_1、a_2、\ dots、a_N)が含まれています。(a_i = 1)の場合、(i)番目の都市には食料があります。(a_i = 0)の場合、(i)番目の都市には食料がありません。

次の(M)行には、それぞれ3つの整数(X)、(Y)、および(W)((X \ ne Y)、(1 \ le W \ le 10 ^ 9))が含まれており、 (X)番目と(Y)番目の都市で、食料費は(W)です。(X)と(Y)の各ペアの間に最大で1つの道路が存在することが保証されます。

次の行には、整数(Q)((1 \ le Q \ le 10 ^ 5))、クエリの数が含まれています。

次の(Q)行には、それぞれ2つの整数(X)と(Y)が含まれており、各クエリの開始都市と宛先都市を示しています。(X)と(Y)の両方が食料供給のある都市であることが保証されています。

出力
クエリごとに、最小の食料容量を示す1つの整数を含む1行を出力します。

サンプル入力
6 7
0 1 0 1 0 1 1
1 2 2
1 1 4
2 3 2
4 5 4
3 6 10
3 5 2
5 6 5
2
1 6
1 5
出力例
5
4

質問:
無向グラフでは、いくつかのポイントがエネルギーでいっぱいになる可能性があり(初期エネルギー値になる)、歩行中にエネルギーを負の数に減らすことはできません。クエリごとに2つのポイントが与えられ、ポイントAからポイントBまで残せる最小エネルギーを見つけます(初期エネルギーを決定する必要があります)。
あなたが尋ねるポイントが請求できるすべてのポイントであることを確認してください

アイデア:
ソリューションを読んだ後、MSTは、2つのポイント間のパスの最大(小さい)側も維持できることに気付きました。これは、毎回最小スパンツリーによって維持される接続が、接続された2つのコンポーネントの最短側であるためです。(グラフ理論は忘れられています。

定義d [x] d [x]d [ x ]は、xから最も近い充電パイルまでの距離です。次に、側面(x、y、w)を(x、y、w + d [x] + d [y])に置き換えます。これは、この側面の最小コストを表します。 。このとき、問題は2点パスの最小エッジを見つけることになるので、最小スパンツリーがメンテナンスに使用されます。

初期エネルギーをVと仮定しているので、始点と終点に到達するためのエネルギーはすべてVであるため、本質的にはVを最小化する必要があります。このポイントが任意のポイントに達した後、このポイントの現在のエネルギーをできるだけ大きくして、必要な初期エネルギーVをできるだけ小さくするために、いくつかの操作を実行する必要があります。

したがって、エッジx-> yの場合、エッジの重みw。最初にxから最も近い充電パイルまで歩いてから戻ります。このとき、エネルギーはポイントx、VVで最大になります。Vは少なくともd [x] d [x]d [ x ]このエッジをポイントBに渡した後、Vは少なくともd [x] + wd [x] + wになりますd [ x ]+wこの時点でポイントBに到達しますが、ポイントBから開始する必要があるため、ポイントBのエネルギーを最大化する必要があり、エネルギーが負であることを保証できません。したがって、この時点でVは少なくともd [x] + d [y] + wd [x ] + d [y] + wd [ x ]+d [ y ]+w、これは初期エネルギーVに対するこのエッジの限界です。

最小初期エネルギーである2点パス制限の最大値を見つけます。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;

typedef long long ll;
const int maxn = 4e5 + 7;

int head[maxn],nex[maxn],to[maxn],tot;
ll val[maxn];
int vis[maxn];
ll d[maxn];
int a[maxn];
priority_queue<pair<ll,int>>q;

void add(int x,int y,ll z) {
    
    
    to[++tot] = y;
    nex[tot] = head[x];
    val[tot] = z;
    head[x] = tot;
}

void dijkstra() {
    
    
    while(!q.empty()) {
    
    
        int now = q.top().second;q.pop();
        if(vis[now]) continue;
        vis[now] = 1;
        for(int i = head[now];i;i = nex[i]) {
    
    
            int v = to[i];
            ll w = val[i];
            if(d[v] > d[now] + w) {
    
    
                d[v] = d[now] + w;
                q.push({
    
    -d[v],v});
            }
        }
    }
}

struct Edge {
    
    
    int x,y;
    ll w;
}edges[maxn];

int cmp(Edge a,Edge b) {
    
    
    return a.w < b.w;
}

int fa[maxn][20];
ll fw[maxn][20];
int f[maxn];

void dfs(int x) {
    
    
    for(int i = head[x];i;i = nex[i]) {
    
    
        int v = to[i];
        ll w = val[i];
        if(!d[v]) {
    
    
            d[v] = d[x] + 1;
            fa[v][0] = x;
            fw[v][0] = w;
            dfs(v);
        }
    }
}

int findset(int x) {
    
    
    if(f[x] == x) return x;
    return f[x] = findset(f[x]);
}

ll lca(int x,int y) {
    
    
    ll ans = 0;
    if(d[x] < d[y]) {
    
     //保证x是最深的
        swap(x,y);
    }
    
    for(int i = 19;i >= 0;i--) {
    
     //x跳到和y一样高
        if(d[fa[x][i]] >= d[y]) {
    
    
            ans = max(ans,fw[x][i]);
            x = fa[x][i];
        }
    }

    if(x == y) {
    
    
        return ans;
    }
    for(int i = 19;i >= 0;i--) {
    
     //两个小朋友一起跳
        if(fa[x][i] != fa[y][i]) {
    
    
            ans = max(fw[x][i],ans);
            ans = max(fw[y][i],ans);
            x = fa[x][i];
            y = fa[y][i];
        }
    }
    ans = max(ans,max(fw[x][0],fw[y][0]));
    return ans;
}

int main() {
    
    
    int n,m;scanf("%d%d",&n,&m);
    memset(d,0x3f,sizeof(d));
    for(int i = 1;i <= n;i++) {
    
    
        scanf("%d",&a[i]);
        if(a[i]) {
    
    
            q.push({
    
    0,i});
            d[i] = 0;
        }
    }
    for(int i = 1;i <= m;i++) {
    
    
        int x,y,w;scanf("%d%d%d",&x,&y,&w);
        edges[i] = {
    
    x,y,w};
        add(x,y,w);
        add(y,x,w);
    }
    
    dijkstra();
    for(int i = 1;i <= m;i++) {
    
    
        edges[i].w += d[edges[i].x] + d[edges[i].y];
    }
    sort(edges + 1,edges + 1 + m,cmp);

    tot = 0;
    memset(d,0,sizeof(d));
    memset(head,0,sizeof(head));
    
    for(int i = 1;i <= n;i++) f[i] = i;
    for(int i = 1;i <= m;i++) {
    
    
        int x = edges[i].x,y = edges[i].y;
        ll w = edges[i].w;
        int rx = findset(x),ry = findset(y);
        if(rx != ry) {
    
    
            f[rx] = ry;
            add(x,y,w);
            add(y,x,w);
        }
    }
    
    fa[1][0] = 0;
    d[1] = 1;
    dfs(1);
    for(int i = 1;i <= n;i++) {
    
    
        if(!d[i]) dfs(i);
    }
    
    for(int i = 1;i <= 19;i++) {
    
    
        for(int j = 1;j <= n;j++) {
    
    
            fa[j][i] = fa[fa[j][i - 1]][i - 1];
            fw[j][i] = max(fw[fa[j][i - 1]][i - 1],fw[j][i - 1]);
        }
    }
    
    int T;scanf("%d",&T);
    while(T--) {
    
    
        int x,y;scanf("%d%d",&x,&y);
        printf("%lld\n",lca(x,y));
    }
    return 0;
}

おすすめ

転載: blog.csdn.net/tomjobs/article/details/109072969