1. 程式人生 > >P1967 貨車運輸(Tarjan做法)

P1967 貨車運輸(Tarjan做法)

傳送門
這個問題叫做『最大瓶頸路徑』
最大瓶頸路徑一定存在於最大生成樹中/反證法:如果最大瓶頸路徑不存在與最大生成樹中。
這些不在最大生成樹中的邊會和最大生成樹形成環。
我們刪掉環上最小的邊,保留這一條邊,會得到一棵新的更大的生成樹。
這與原來那棵樹是最大生成樹矛盾了。
注意,最短路不一定在最小生成樹上(如一個環的情況)

現在問題轉變為求樹上兩點的最小邊權問題了。
很明顯是從u->LCA(u,v)->v這條路能使最小邊權最大。
我們先用Tarjan求出LCA.記錄下來。
用一個數組記錄DFS遍歷的前一個節點和邊權,然後遞迴分別找u->LCA(u,v)的最小權值,v->LCA(u,v)的最小權值
兩者取min即為答案。
需要注意的是,加入詢問的兩個點不在一顆樹上,Tarjan跑出的LCA其實是不對的,所以要在最後輸出時去判斷一下是否在一棵樹中。

#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxm=510000;
struct Node{
    int net;
    int to;
    int cost;
    int lca;
    int ans;
};
bool vis[maxm];
Node edge[2][2*maxm];
int cnt[2],head[2
][maxm],n,m; int fatt[maxm],fat[maxm],dis[maxm]; int Dis1,Dis2; int ansx[maxm],ansy[maxm]; int fatx[maxm]; void add(int id,int x,int y,int c) { edge[id][++cnt[id]].to=y; edge[id][cnt[id]].cost=c; edge[id][cnt[id]].net=head[id][x]; head[id][x]=cnt[id]; } struct node{ int x,y,z; }a[maxm]; int
find1(int x) { if(x==fatt[x]) return x; return fatt[x]=find1(fatt[x]); } int findx(int x) { if(x==fatx[x]) return x; else return fatx[x]=findx(fatx[x]); } int find(int id,int x,int fs) { if(id==0) { Dis1=1e17; while(fat[x]!=x&&fs!=x) { Dis1=min(Dis1,dis[x]); x=fat[x]; } return x; } else { Dis2=1e17; while(fat[x]!=x&&fs!=x) { Dis2=min(Dis2,dis[x]); x=fat[x]; } return x; } } bool comp(const node&x,const node&y) { return x.z>y.z; } void dfs(int x) { vis[x]=1; fat[x]=x; fatx[x]=x; for(int K=head[0][x];K;K=edge[0][K].net) { int p=edge[0][K].to; if(vis[p]) continue; dfs(p); fatx[p]=x; fat[p]=x; dis[p]=edge[0][K].cost; } for(int i=head[1][x];i;i=edge[1][i].net) { int p=edge[1][i].to; if(!vis[p]) continue; //int ans=min(Dis1,Dis2); //edge[1][i].ans=ans; edge[1][i].lca=findx(p); ansx[i]=x,ansy[i]=p; if(i%2) edge[1][i+1].lca=edge[1][i].lca,ansx[i+1]=x,ansy[i+1]=p; else edge[1][i-1].lca=edge[1][i].lca,ansx[i-1]=x,ansy[i-1]=p; } } int main() { //freopen("truck.in","r",stdin); //freopen("truck.out","w",stdout); int maxf=0; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) fatt[i]=i,fat[i]=i,fatx[i]=i; for(int i=1,x,y,z;i<=m;i++) scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z); sort(a+1,a+m+1,comp); int tot=0; for(int i=1;i<=m&&tot!=n-1;i++) if(find1(a[i].x)!=find1(a[i].y)) { fatt[find1(a[i].x)]=find1(a[i].y); add(0,a[i].x,a[i].y,a[i].z);add(0,a[i].y,a[i].x,a[i].z); tot++; } int t; scanf("%d",&t); for(int i=1;i<=t;i++) { int x,y; scanf("%d%d",&x,&y); add(1,x,y,0),add(1,y,x,0); } for(int i=1;i<=n;i++) if(!vis[i]) dfs(i); for(int i=1;i<=t;i++) if(findx(ansx[2*i])!=findx(ansy[2*i])) printf("-1\n"); else { find(0,ansx[2*i],edge[1][2*i].lca); find(1,ansy[2*i],edge[1][2*i].lca); printf("%d\n",min(Dis1,Dis2)); } }