【BZOJ-4289】Tax 最短路 + 技巧建图
4289: PA2012 Tax
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 168 Solved: 69
[Submit][Status][Discuss]
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
HINT
Source
Solution
比较有技巧的建图
首先考虑暴力点的建图:
把每条无向边拆成两条有向边.把每条边看成一个点,对于两条边a->b,b->c
在这两条边之间连有向边,边权为这两条边的权值的较大值.
新建源点S,汇点T, S向所有从1连出去的边连边,所有指向n的边向T连边. 求S->T的最短路即可.
这样的复杂度会达到$O(m^{2})$
考虑优化一下,有个类似网络流中补流思想的方法:
考虑利用差值来建边.
依然把每条边x-y拆成x->y,y->x.
枚举每个中转点x. 将x的出边按权值排序,x的每条入边向对应的出边连该边权值的边,x的每条出边向第一个比它大的出边连两边权差值的边,x的每条出边向第一个比它小的出边连权值为0的边. 新建源汇S,T S向每条1的出边连权值为该边边权的边.每条n的入边向T连该边权值的边.
跑S->T的最短路即可.
这样的复杂度是$O(mlogm)$就可以AC
顺带提一句,用Dijkstra效率很快
Code
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<queue> using namespace std; void Freopen() {freopen("travel.in","r",stdin); freopen("travel.out","w",stdout);} #define Pa pair<long long,int> inline int read() { int x=0,f=1; char ch=getchar(); while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();} while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();} return x*f; } #define MAXN 200010 #define INF 1LL<<60 int N,M; struct EdgeNode{int next,to,val;}edge[MAXN<<1],road[MAXN<<3]; int head[MAXN],cnt=1,first[MAXN<<1],tot=1; inline void AddEdge(int u,int v,int w) {cnt++; edge[cnt].next=head[u]; head[u]=cnt; edge[cnt].to=v; edge[cnt].val=w;} inline void InsertEdge(int u,int v,int w) {AddEdge(u,v,w); AddEdge(v,u,w);} inline void AddRoad(int u,int v,int w) {tot++; road[tot].next=first[u]; first[u]=tot; road[tot].to=v; road[tot].val=w;} inline bool cmp(int x,int y) {return edge[x].val<edge[y].val;} priority_queue<Pa,vector<Pa>,greater<Pa> >q; int S,T; long long dis[MAXN<<1]; void Dijkstra() { for (int i=S; i<=T; i++) dis[i]=INF; q.push(make_pair(0,S)); dis[S]=0; while (!q.empty()) { int now=q.top().second; long long Dis=q.top().first; q.pop(); if (Dis>dis[now]) continue; for (int i=first[now]; i; i=road[i].next) if (dis[now]+road[i].val<dis[road[i].to]) dis[road[i].to]=dis[now]+road[i].val, q.push(make_pair(dis[road[i].to],road[i].to)); } } int st[MAXN<<1],tp; void BuildGraph() { S=1,T=2*(M+1); for (int i=1; i<=N; i++) { tp=0; for (int j=head[i]; j; j=edge[j].next) st[++tp]=j; sort(st+1,st+tp+1,cmp); for (int j=1; j<=tp; j++) { int now=st[j],suc=st[j+1]; if (edge[now].to==N) AddRoad(now,T,edge[now].val); if (i==1) AddRoad(S,now,edge[now].val); AddRoad(now^1,now,edge[now].val); if (j<tp) AddRoad(now,suc,edge[suc].val-edge[now].val),AddRoad(suc,now,0); } } } int main() { // Freopen(); N=read(),M=read(); for (int x,y,z,i=1; i<=M; i++) x=read(),y=read(),z=read(),InsertEdge(x,y,z); BuildGraph(); Dijkstra(); printf("%lld\n",dis[T]); return 0; }
小压一波常数,成功跑到rank2..想怒压300ms到rank1时,失误反而慢了300ms....23333
Orz现场裸跑最短路80分的梁哥....
貌似还有一种类似于DP的方法?
——It's a lonely path. Don't make it any lonelier than it has to be.