题解 P1339 【[USACO09OCT]热浪Heat Wave】

lemondinosaur

2018-11-14 20:59:32

Solution

# 前言 实际上这道题就是一道单源最短路径的题目,当然还是有很多方法的,就比如SPFA,dijkstra(标准,堆优化)等等,然而当我看到dalao线段树的题解的时候,只剩下了Orz,现在就来讲一下我的思路吧! --- # 思路 这次我依然不走寻常路,用的是dijkstra+zkw线段树优化,这份代码是这周日写的,所以说还是比较神奇的,主要思想就是维护最小值,然而我的思路是这样的,首先先在叶子节点处更新dis的最小值以及dis最小值的位置,具体可以表现在![](https://cdn.luogu.com.cn/upload/pic/43528.png) 时间复杂度大概是$O(n)$的,然后删除该点的时候需要单点修改,在松弛操作完成后还要进行修改,总的来说时间复杂度是$O(n+elogn)$ --- # 代码(25ms不加O2)(还是比较慢的) ```cpp #include <cstdio> #include <cctype> #include <algorithm> #define rr register using namespace std; const int inf=707406378; struct node{int y,w,next;}e[12501]; int n,m,s,t,bas=1,k=1,ls[2511],w[8201],p[8201],dis[2511]; inline signed iut(){ rr int ans=0; rr char c=getchar(); while (!isdigit(c)) c=getchar(); while (isdigit(c)) ans=(ans<<3)+(ans<<1)+c-48,c=getchar(); return ans; } inline void add(int x,int y,int w){e[++k]=(node){y,w,ls[x]}; ls[x]=k;} inline void update(int x){ w[x]=(w[x<<1]<w[x<<1|1])?w[x<<1]:w[x<<1|1];//取左子右子最小值 p[x]=(w[x<<1]>w[x<<1|1])?p[x<<1|1]:p[x<<1];//实时更新编号 } signed main(){ n=iut(); m=iut(); s=iut(); t=iut(); for (rr int i=1;i<=m;++i){ rr int x=iut(),y=iut(),w=iut(); add(x,y,w); add(y,x,w); } while ((bas<<=1)<n+2);//由于区间修改需要扩张,所以实际上空间要多1位,总之应在2^x>n的范围内 fill(w+1,w+2+(bas<<1),inf); fill(dis+1,dis+1+n,inf);//全部初始话位一个很大的值 fill(p+1,p+2+(bas<<1),inf); w[s+bas-1]=dis[s]=0;//那么 for (rr int i=0;i<n;++i) p[bas+i]=i+1;//输入编号 for (rr int i=bas-1;i;--i) update(i);//记住bas表示的是当为满二叉树时除最后一层的个数+1,所以说要减掉1 while (w[1]<inf){//也就是说都没有访问过 rr int x=p[1],y=p[1]+bas-1; w[y]=inf; for (y>>=1;y;y>>=1) update(y);//删除现在的最小点 for (rr int i=ls[x];i;i=e[i].next) if (dis[e[i].y]>dis[x]+e[i].w){//这一段长的挺像SPFA的 dis[e[i].y]=dis[x]+e[i].w; rr int t=e[i].y+bas-1; for (w[t]=dis[e[i].y],t>>=1;t;t>>=1) update(t);//松弛操作后新到达的节点插入zkw线段树中 } } return !printf("%d",dis[t]);//输出s到t的距离 } ```