[bzoj2407]探險——重構圖+最短路 dalaos' blogs Some Links
阿新 • • 發佈:2018-11-03
題目大意:
給定一個無向圖,每一條邊正著和反著都有一個邊權,求一條不經過重複邊的路徑,使得邊權和最小。
思路:
這個題目的思路比較巧妙
網路上的題解只有做法,沒有詳細地解釋。
考慮最暴力的方法,對於1號點能夠直接到達的每一個點,把它和1號點的邊刪掉以後,以這個點為源點跑最短路。
但是這樣會T飛。
不妨先放下不能重複走的限制,建立一個新的虛擬匯點t=n+1,然後將所有連向1的邊轉化成連向t的邊。然後對於1到t跑最短路演算法
這樣子做顯然有一些點問題,有的路徑可能剛剛才走出去就又回來了,這樣是不合法的。
例如一條邊(1,v,w),在轉移中可能會是這個樣子的
那麼如果我們將(1,v,w)給去掉,將可能從(1,v,w)這條邊出發的路徑轉化為一些邊(1,u,dis[1…u]) (1,v,w)
this path
使得這些u存在於所有
的路徑上,並且對於所有的u,都有1到u的最短路的第一個點不是v。
這樣以後我們再去求最短路,便不用擔心一條邊跑出去再跑回去的問題,因為有了上面的限制,通過這條邊出去之後再回去一定不是最短的。
慮怎麼建立上面所說的等效邊,可以對於原圖做一次最短路,記錄每個點的最短路徑的第一個點f[u],列舉每一條邊(u,v,w),如果f[u]!=f[v],那麼則說明這是f[u]和f[v]的範圍的分界處,在這裡分別對f[u]和f[v]都建立等效邊即可。
當然如果u=1或者v=1的情況需要特殊考慮,但是方法是類似的。具體方法可以參考hzwer的部落格。
#include<bits/stdc++.h>
#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define pii pair<int,int>
#define fi first
#define se second
#define mk make_pair
typedef long long ll;
using namespace std;
void File(){
freopen("bzoj2407.in","r",stdin);
freopen("bzoj2407.out","w",stdout);
}
template<typename T>void read(T &_){
T __=0,mul=1; char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')mul=-1;
ch=getchar();
}
while(isdigit(ch))__=(__<<1)+(__<<3)+(ch^'0'),ch=getchar();
_=__*mul;
}
const int maxn=1e4+10;
const int maxm=2e5+10;
int n,m,dis[maxn],fr[maxn];
int beg[maxn],from[maxm<<1],to[maxm<<1],las[maxm<<1],w[maxm<<1],cnte=1;
priority_queue< pii,vector<pii>,greater<pii> >qu;
void add(int u,int v,int val){
las[++cnte]=beg[u]; beg[u]=cnte; to[cnte]=v; from[cnte]=u; w[cnte]=val;
}
void Dijkstra(){
memset(dis,63,sizeof(dis));
memset(fr,0,sizeof(fr));
dis[1]=0; qu.push(mk(0,1));
while(!qu.empty()){
int u=qu.top().se,d=qu.top().fi;
qu.pop();
if(d!=dis[u])continue;
for(int i=beg[u];i;i=las[i]){
int v=to[i];
if(d+w[i]<dis[v]){
fr[v]= u==1 ? i/2 : fr[u];
dis[v]=d+w[i];
qu.push(mk(dis[v],v));
}
}
}
}
void rebuild(){
int sz=cnte; cnte=0;
memset(beg,0,sizeof(beg));
REP(i,2,sz){
if(from[i]==1){
if(fr[to[i]]!=i/2)
add(from[i],to[i],w[i]);
}
else if(to[i]==1){
if(fr[from[i]]==i/2)
add(from[i],n+1,w[i]);
else add(1,n+1,dis[from[i]]+w[i]);
}
else{
if(fr[from[i]]==fr[to[i]])
add(from[i],to[i],w[i]);
else add(1,to[i],dis[from[i]]+w[i]);
}
}
}
int main(){
File();
read(n); read(m);
int u,v,val0,val1;
REP(i,1,m){
read(u),read(v),read(val0),read(val1);
add(u,v,val0),add(v,u,val1);
}
Dijkstra();
rebuild();
Dijkstra();
printf("%d\n",dis[n+1]);
return 0;
}