BZOJ4289: PA2012 Tax

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

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$的最短路即可。

 

注:
  1. 此题卡$SPFA$!此题卡$SPFA$!!此题卡$SPFA$!!!重要的事情说三遍!!!所以还是乖乖写堆优化$dijkstra$吧。。。
  2. 记得与路径长度有关的数组、变量都要开$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;
}

 

posted @ 2018-08-29 22:01  符拉迪沃斯托克  阅读(247)  评论(0编辑  收藏  举报
Live2D