[BZOJ4289] [PA2012] Tax 解题报告 (最短路+差分建图)
题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=4289
4289: PA2012 Tax
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1029 Solved: 310
[Submit][Status][Discuss]
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
1 2 5
1 3 2
2 3 1
2 4 4
3 4 8
Sample Output
12
比较有技巧的建图
首先考虑暴力点的建图:
把每条无向边拆成两条有向边.把每条边看成一个点,对于两条边a->b,b->c
在这两条边之间连有向边,边权为这两条边的权值的较大值.
新建源点S,汇点T, S向所有从1连出去的边连边,所有指向n的边向T连边. 求S->T的最短路即可.
这样的复杂度会达到O(m2)
考虑优化一下:
考虑利用差值来建边.
依然把每条边x-y拆成x->y,y->x.
枚举每个中转点x. 将x的出边按权值排序,x的每条入边向对应的出边连该边权值的边(i对应i^1)),x的每条出边向第一个比它大的出边连两边权差值的边,x的每条出边向第一个比它小的出边连权值为0的边. 新建源汇S,T S向每条1的出边连权值为该边边权的边.每条n的入边向T连该边权值的边.
仔细看图,L2>L1,发现从S到T的路径走的一定是L2,而不是L1;注意结合上述的建图过程(红色边)
跑S->T的最短路即可.
这样的复杂度是O(mlogm)就可以AC
我的SPFA被T了,估计没写错只是被卡了
以上内容部分参考DaD3zZ大佬的博客
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<queue> #define ll long long using namespace std; const int N=200000+15; int n,m,tot1,tot2,S,T; int head[N],h[N<<1],st[N<<1],vis[N<<1]; ll dist[N<<1]; struct EDGE { int to;int next;int l; }edge[N<<1],e[N<<3]; inline int read() { char ch=getchar(); int s=0,f=1; while (!(ch>='0'&&ch<='9')) {if (ch=='-') f=-1;ch=getchar();} while (ch>='0'&&ch<='9') {s=(s<<3)+(s<<1)+ch-'0';ch=getchar();} return s*f; } void init() { memset(head,-1,sizeof(head)); memset(h,-1,sizeof(h)); memset(dist,0x3f3f3f3f,sizeof(dist)); } void addedge(int x,int y,int l) { edge[tot1]=(EDGE){y,head[x],l}; head[x]=tot1++; } bool cmp(int x,int y) {return edge[x].l<edge[y].l;} void addroad(int x,int y,int l) { e[tot2]=(EDGE){y,h[x],l}; h[x]=tot2++; } void build() { S=2*(m+1)+15;T=2*(m+1)+16; for (int i=1;i<=n;i++) { int tp=0; for (int j=head[i];j!=-1;j=edge[j].next) st[++tp]=j; sort(st+1,st+1+tp,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].l); if (i==1) addroad(S,now,edge[now].l); addroad(now^1,now,edge[now].l); if (j<tp) {addroad(now,suc,edge[suc].l-edge[now].l);addroad(suc,now,0);} } } } priority_queue<pair<ll,int>,vector<pair<ll,int> >,greater<pair<ll,int> > > q; void Dijkstra() { q.push(make_pair(0,S)); dist[S]=0; while (!q.empty()) { int now=q.top().second; ll Dis=q.top().first;q.pop(); if (Dis>dist[now]) continue; for (int i=h[now];i!=-1;i=e[i].next) if (dist[now]+e[i].l<dist[e[i].to]) { dist[e[i].to]=dist[now]+e[i].l; q.push(make_pair(dist[e[i].to],e[i].to)); } } } int main() { n=read();m=read(); init(); for (int i=1;i<=m;i++) { int x=read(),y=read(),l=read(); addedge(x,y,l);addedge(y,x,l); } build(); Dijkstra(); printf("%lld\n",dist[T]); return 0; }
星星之火,终将成燎原之势