BZOJ4289: PA2012 Tax
Description
给出一个N个点M条边的无向图,经过一个点的代价是进入和离开这个点的两条边的边权的较大值,求从起点1到点N的最小代价。起点的代价是离开起点的边的边权,终点的代价是进入终点的边的边权
N<=100000
M<=200000
Input
Output
Sample Input
4 5
1 2 5
1 3 2
2 3 1
2 4 4
3 4 8
1 2 5
1 3 2
2 3 1
2 4 4
3 4 8
Sample Output
12
题解Here!
这个题一眼看去感觉不可做啊。。。
关键就是建图。。。
这是一个比较有技巧的建图方式。
首先考虑暴力点的建图:
常规操作:把每条无向边拆成两条有向边。
把每条边看成一个点,对于两条边$a->b,b->c$:
在这两条边之间连有向边,边权为这两条边的权值的较大值。
新建源点$S$,汇点$T$,$S$向所有从$1$连出去的边连边,所有指向$n$的边向$T$连边。
求$S->T$的最短路即可。
这个方式显然复杂度是$O(m^2)$的,铁定$TLE$。。。
所以考虑骚操作优化。
我们用类似网络流中补流思想的方法:考虑利用差值来建边。
依然把每条边$x-y$拆成$x->y,y->x$。
枚举每个中转点$x$。
将$x$的出边按权值排序,$x$的每条入边向对应的出边连该边权值的边,$x$的每条出边向第一个比它大的出边连两边权差值的边,$x$的每条出边向第一个比它小的出边连权值为$0$的边。
新建源汇$S,T$,$S$向每条$1$的出边连权值为该边边权的边。
每条$n$的入边向$T$连该边权值的边。
跑$S->T$的最短路即可。
注:
- 此题卡$SPFA$!此题卡$SPFA$!!此题卡$SPFA$!!!重要的事情说三遍!!!所以还是乖乖写堆优化$dijkstra$吧。。。
- 记得与路径长度有关的数组、变量都要开$long\ long$!
附代码:
#include<iostream> #include<algorithm> #include<cstdio> #include<queue> #define MAXN 400010 #define MAX (1LL<<60) using namespace std; int n,m,c=1,d=2,s,t; int head[MAXN],h[MAXN],stack[MAXN]; long long path[MAXN]; bool vis[MAXN]; struct Edge{ int next,to; long long w; }edge[MAXN],a[MAXN<<2]; struct node{ int x; long long dis; bool operator <(const node &p)const{ return dis>p.dis; } }; priority_queue<node> q; inline int read(){ int date=0,w=1;char c=0; while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();} while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();} return date*w; } inline bool cmp(const int &p,const int &q){ return edge[p].w<edge[q].w; } inline int relax(int u,int v,long long w){ if(path[v]>path[u]+w){ path[v]=path[u]+w; return 1; } return 0; } inline void add_edge(int u,int v,long long w){ edge[d].to=v;edge[d].w=w;edge[d].next=h[u];h[u]=d++; edge[d].to=u;edge[d].w=w;edge[d].next=h[v];h[v]=d++; } inline void add(int u,int v,long long w){ a[c].to=v;a[c].w=w;a[c].next=head[u];head[u]=c++; } void dijkstra(){ node u,v; for(int i=s;i<=t;i++){path[i]=MAX;vis[i]=false;} u.x=s;u.dis=path[s]=0; q.push(u); while(!q.empty()){ u=q.top(); q.pop(); if(!vis[u.x]){ vis[u.x]=true; for(int i=head[u.x];i;i=a[i].next){ v.x=a[i].to; if(!vis[v.x]){ path[v.x]=min(path[v.x],path[u.x]+a[i].w); v.dis=u.dis+a[i].w; q.push(v); } } } } } void build(){ int top; for(int i=1;i<=n;i++){ top=0; for(int j=h[i];j;j=edge[j].next)stack[++top]=j; sort(stack+1,stack+top+1,cmp); for(int j=1;j<=top;j++){ int now=stack[j],after=stack[j+1]; if(edge[now].to==n)add(now,t,edge[now].w); if(i==1)add(s,now,edge[now].w); add(now^1,now,edge[now].w); if(j<top){ add(now,after,edge[after].w-edge[now].w); add(after,now,0); } } } } void work(){ dijkstra(); printf("%lld\n",path[t]); } void init(){ int u,v,w; n=read();m=read(); s=1;t=((m+1)<<1); for(int i=1;i<=m;i++){ u=read();v=read();w=read(); add_edge(u,v,w); } build(); } int main(){ init(); work(); return 0; }