普通建图的最短路肯定跑不过,我们要尝试优化建图
具体两种方法:
1.线段树优化建图
我们类似于线段树区间操作区间连边
其实每个节点我们分别设一个in和out代表向儿子连边,以及儿子向自己连边即可
2.倍增优化建图
同样的道理用类似st表维护,但是会在操作很多点数较少的时候有较大的优势,因为每次操作两条边就可以搞定,
看着大家都写的是线段树优化建图,我就写了个st表建图,建完图用dij跑一下就ok了
代码如下:
#include<bits/stdc++.h> #define LL long long #define pii pair<LL,int> using namespace std; const int N=1e5+5,S=4e6+5,M=S<<2; const LL Inf=1e18; struct Edge{int to,w,nxt;}e[M]; int n,m,s,fst[S],tote,lg[N],in[N][20],out[N][20],id;LL dis[S];bool vis[S]; void adde(int u,int v,int w){e[++tote]=(Edge){v,w,fst[u]};fst[u]=tote;} priority_queue<pii>q; void dij(){ for(int i=0;i<S;i++)dis[i]=Inf; q.push(pii(0,s));dis[s]=0; while(!q.empty()){ int u=q.top().second;q.pop(); if(vis[u])continue;vis[u]=true; for(int i=fst[u],v,w;i;i=e[i].nxt){ v=e[i].to;w=e[i].w; if(dis[v]>dis[u]+w)dis[v]=dis[u]+w,q.push(pii(-dis[v],v)); } } } int main(){ scanf("%d%d%d",&n,&m,&s); lg[0]=-1;for(int i=1;i<=n;i++)lg[i]=lg[i>>1]+1; for(int i=1;i<=n;i++)in[i][0]=out[i][0]=i; id=n; for(int k=1;k<=18;k++)for(int i=1;i+(1<<k)-1<=n;i++){ out[i][k]=++id;in[i][k]=++id; adde(out[i][k],out[i][k-1],0);adde(out[i][k],out[i+(1<<k-1)][k-1],0); adde(in[i][k-1],in[i][k],0);adde(in[i+(1<<k-1)][k-1],in[i][k],0); } while(m--){ int opt,u,v,l,r,w;scanf("%d",&opt); if(opt==1)scanf("%d%d%d",&u,&v,&w),adde(u,v,w); if(opt==2){ scanf("%d%d%d%d",&u,&l,&r,&w); int t=lg[r-l+1]; adde(u,out[l][t],w);adde(u,out[r-(1<<t)+1][t],w); } if(opt==3){ scanf("%d%d%d%d",&u,&l,&r,&w); int t=lg[r-l+1]; adde(in[l][t],u,w);adde(in[r-(1<<t)+1][t],u,w); } } dij(); for(int i=1;i<=n;i++)if(dis[i]>=Inf)printf("-1 ");else printf("%lld ",dis[i]); puts(""); return 0; }