bzoj 4289: 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
HINT
Source
首先考虑暴力的做法,把无向边拆为两条,把边当做点,如果两条边之间有交点,就连边,边权为两个点所代表的边的边权较大者;
然后这样的边数为n^2,然后我们可以考虑用差分的思想优化连边;
我们把原图中每个点的出边进行排序,然后边权小的点往边权大的点连差值的权值,然后大的往小的连0的权值,然后每条边对应的两个点连边权为原图权值的边;
这样做相当于从小往大走,用差分累加答案,然后从大的往小走最大值就是自己,所以权值为0,
然后通过第三种边完成了入边和出边的转化,中间的边权代表入边的边权,并且割断了该入边与上一条出边的关系;
然后特殊处理S,T即可,Spfa 跑不过,系统堆Dijkstra跑得挺快的;
//MADE BY QT666 #include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<queue> #define RG register using namespace std; typedef long long ll; const int N=500050; const int M=2000050; int n,m,cnt=1,tot,sta,T,tmp,to[M],edg[M],nxt[M],q[N],val[N],id[N],vis[N]; ll dis[N]; struct data{int x,y,z;}a[N]; struct graph{ int head[N]; void lnk(int x,int y,int z){ to[++tot]=y; edg[tot]=z; nxt[tot]=head[x]; head[x]=tot; } }g1,g2; bool cmp(const data &u,const data &v){return u.z<v.z;} struct date{ int x;ll d; bool operator <(const date b) const{return d>b.d;} }; priority_queue <date> Q; void Dijkstra(){ memset(dis,127,sizeof(dis)); dis[1]=0;Q.push((date){1,0}); while(!Q.empty()){ int x=Q.top().x;Q.pop(); if(vis[x])continue;vis[x]=1; for(RG int i=g2.head[x];i;i=nxt[i]){ int y=to[i]; if(dis[y]>dis[x]+edg[i]){ dis[y]=dis[x]+edg[i],Q.push((date){y,dis[y]}); } } } } int main(){ scanf("%d%d",&n,&m);RG int i,j,x,y,z,p; for(i=1;i<=m;i++){ scanf("%d%d%d",&x,&y,&z); val[++cnt]=z; val[++cnt]=z; g1.lnk(x,cnt,cnt-1);g1.lnk(y,cnt-1,cnt); } for(i=2;i<n;i++){ for(p=g1.head[i],tmp=0; p; p=nxt[p]) a[++tmp]=(data){to[p],edg[p],val[to[p]]}; if(!tot) continue; sort(a+1,a+tmp+1,cmp); for(j=1;j<=tmp;j++) g2.lnk(a[j].x,a[j].y,a[j].z); for(j=1;j<tmp;j++){ g2.lnk(a[j].y,a[j+1].y,a[j+1].z-a[j].z); g2.lnk(a[j+1].y,a[j].y,0); } } T=++cnt; for(p=g1.head[1];p;p=nxt[p]) g2.lnk(1,edg[p],val[edg[p]]); for(p=g1.head[n];p;p=nxt[p]) g2.lnk(to[p],T,val[edg[p]]); Dijkstra();printf("%lld\n",dis[T]); return 0; }