bzoj4289 Tax

Description

給出一個N個點M條邊的無向圖,通過一個點的代價是進入和離開這個點的兩條邊的邊權的較大值,求從起點1到點N的最小代價。起點的代價是離開起點的邊的邊權,終點的代價是進入終點的邊的邊權
N<=100000
M<=200000

Sample Input

4 5
1 2 5
1 3 2
2 3 1
2 4 4
3 4 8

Sample Output

12
 
 
很容易想到暴力:化邊爲點,每兩個點中間的邊權爲兩個原來邊的更大的權,如圖,紅色的點是新點:
 
可是,若是出現了菊花圖,那麼新邊的個數會變成M^2,原地爆炸,咱們必須優化建邊。
考慮把原來的無向邊,變成兩條有向邊,也就是在新圖上把一個點拆成兩個點,這兩個點之間的邊權是原邊的邊權。
對於每個原圖上的點,把它的全部出邊進行排序,每條出邊從小到大連一條兩個邊權之差的邊,如圖:
 
這樣運用查分建圖,就比如,我要過這個點,原來是一塊兒交了錢,如今建完圖是先交進入的錢,再將出邊和入邊的差補交上去。
而後,再將新圖創建S、T分別是源點的匯點。將S連向全部原圖起點的出邊,全部原圖終點的入邊連向T。
最後圖會成爲這個樣子:
固然,最後跑一邊Dijkstra,SPFA會被卡。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue> 
#include <cstdlib>
#define REP(i,k,n)  for(int i=k;i<=n;i++)
#define in(a) a=read()
#define MAXN 400040
using namespace std;
inline int read(){
    int x=0,f=1;
    char ch=getchar();
    for(;!isdigit(ch);ch=getchar())
        if(ch=='-')
            f=-1;
    for(;isdigit(ch);ch=getchar())
        x=x*10+ch-'0';
    return x*f;
}
int n,m;
int S,T;
int total,head[MAXN],to[MAXN*2],nxt[MAXN*2],val[MAXN*2];
int Total,Head[2000000],To[2000000],Nxt[2000000],Val[2000000];
int vis[400010];
long long dis[400010];
struct edge{
    int id,va;
}st[MAXN*2]; 
struct node{
    int a;
    long long b;
    bool operator <(const node &x)const{
        return b>x.b;
    }
};
priority_queue<node> Q; 
inline int change(int x){
    if(x%2==1)  return x+1;
    return x-1;
} 
inline void adl(int a,int b,int c){
    total++;
    to[total]=b;
    val[total]=c;
    nxt[total]=head[a];
    head[a]=total;
    return ;
}
inline void Adl(int a,int b,int c){
    Total++;
    To[Total]=b;
    Val[Total]=c;
    Nxt[Total]=Head[a];
    Head[a]=Total;
    return ;
}
inline bool cmp(edge a,edge b){
    return a.va<b.va;
} 
inline void solve(int u){
    int cnt=0;
    for(int e=head[u];e;e=nxt[e])  st[++cnt].id=e,st[cnt].va=val[e];
    sort(st+1,st+cnt+1,cmp);//對於u的全部出邊排序
    REP(i,1,cnt-1)  Adl(st[i].id,st[i+1].id,st[i+1].va-st[i].va),Adl(st[i+1].id,st[i].id,0);//連查分邊
    for(int e=head[u];e;e=nxt[e])  Adl(change(e),e,val[e]);//每一條出邊連向所對入邊
    return ;
}
inline long long Dijkstra(){
    memset(dis,0x7f,sizeof(dis));
    dis[S]=0;
    node p;
    p.a=S,p.b=0;
    Q.push(p);
    while(!Q.empty()){
        int u=Q.top().a;Q.pop();
        if(vis[u])  continue;
        vis[u]=1;
        for(int e=Head[u];e;e=Nxt[e])
            if(dis[To[e]]>dis[u]+Val[e]){
                dis[To[e]]=dis[u]+Val[e];
                node q;
                q.a=To[e],q.b=dis[To[e]];
                Q.push(q);
            }
    }
    return dis[T];
}
int main(){
    in(n),in(m);
    int a,b,c;
    REP(i,1,m)  in(a),in(b),in(c),adl(a,b,c),adl(b,a,c);
    S=0,T=total+1;
    for(int e=head[1];e;e=nxt[e])  Adl(S,e,val[e]);//處理源點 
    for(int e=head[n];e;e=nxt[e])  Adl(change(e),T,val[e]);//處理匯點
    for(int i=1;i<=n;i++)  solve(i); 
    printf("%d",Dijkstra()); 
    return 0;
}
/*
4 5
1 2 5
1 3 2
2 3 1
2 4 4
3 4 8
*/
相關文章
相關標籤/搜索