1. 程式人生 > >bzoj2125 最短路——仙人掌兩點間距離

bzoj2125 最短路——仙人掌兩點間距離

spa bsp ios bzoj2125 處理 dep 答案 pre 最短路

題目:https://www.lydsy.com/JudgeOnline/problem.php?id=2125

仙人掌!模仿 lyd 的代碼寫的,也算是努力理解了;

主要分成 lca 在環上和不在環上,先縮環(環上的點直接連向最高點),那麽不在環上的 lca 就跟在樹上一樣求法;

在環上的話就先求出環外部分,再計算環內距離;

所以一遍 spfa 求從根出發的最短路,再一遍 dfs 求 dfs 序的 dis ,用來處理環上距離,然後 bfs 計算深度用來倍增求 lca,然後分類求答案即可;

註意邊數就是點的4倍,還要算上縮環時連的邊。

代碼如下:

#include<iostream>
#include
<cstdio> #include<cstring> #include<queue> #include<cmath> using namespace std; int const maxn=10005,maxm=40005;// int n,m,Q,hd[maxn],ct=1,dis[maxn],dist[maxn],cr,col[maxn],tim,dfn[maxn]; int fa[maxn],len[maxn],f[maxn][20],dep[maxn]; bool del[maxm],vis[maxn]; queue<int>q; struct
N{ int to,nxt,w; N(int t=0,int n=0,int w=0):to(t),nxt(n),w(w) {} }ed[maxm]; void add(int x,int y,int z){ed[++ct]=N(y,hd[x],z); hd[x]=ct;} int ab(int x){return x<0?-x:x;} void spfa() { memset(dist,0x3f,sizeof dist); memset(vis,0,sizeof vis); dist[1]=0; q.push(1); vis[1]=1; while(q.size()) {
int x=q.front(); q.pop(); vis[x]=0; for(int i=hd[x],u;i;i=ed[i].nxt) if(dist[u=ed[i].to]>dist[x]+ed[i].w) { dist[u]=dist[x]+ed[i].w; if(!vis[u])vis[u]=1,q.push(u); } } } void make(int x,int e) { int i,y=x; x=ed[e].to; len[++cr]+=ed[e].w; col[y]=cr; del[e]=del[e^1]=1; add(x,y,0); add(y,x,0);//!連向最高點 for(i=fa[y];(y=ed[i^1].to)!=x;i=fa[y]) { len[cr]+=ed[i].w; col[y]=cr; del[i]=del[i^1]=1; add(x,y,0); add(y,x,0);//!連向最高點 } col[x]=cr; len[cr]+=ed[i].w; } void dfs(int x) { dfn[x]=++tim; for(int i=hd[x],u;i;i=ed[i].nxt) { if(!dfn[u=ed[i].to]) { fa[u]=i; dis[u]=dis[x]+ed[i].w; dfs(u); } else if(dfn[u]<dfn[x]&&fa[x]!=(i^1))make(x,i); } } void bfs() { while(q.size())q.pop(); memset(vis,0,sizeof vis); vis[1]=1; q.push(1); dep[1]=1; while(q.size()) { int x=q.front(); q.pop(); for(int i=hd[x],u;i;i=ed[i].nxt) { if(vis[u=ed[i].to]||del[i])continue; vis[u]=1; dep[u]=dep[x]+1; f[u][0]=x; for(int j=1;j<=15;j++)f[u][j]=f[f[u][j-1]][j-1]; q.push(u); } } } int lca(int x,int y) { if(dep[x]<dep[y])swap(x,y); int a=x,b=y; int k=dep[x]-dep[y]; for(int i=0;i<=15;i++) if(k&(1<<i))x=f[x][i]; if(x==y)return dist[a]-dist[b]; for(int i=15;i>=0;i--) if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i]; if(col[x]&&col[x]==col[y]) { int l=ab(dis[x]-dis[y]); return dist[a]-dist[x]+dist[b]-dist[y]+min(l,len[col[x]]-l); //兩個點從環外跳到環上,所以先加上環外部分的 dist,再算環上的最短距離 } return dist[a]+dist[b]-2*dist[f[x][0]]; } int main() { scanf("%d%d%d",&n,&m,&Q); for(int i=1,x,y,z;i<=m;i++) { scanf("%d%d%d",&x,&y,&z); add(x,y,z); add(y,x,z); } spfa(); dfs(1); bfs(); for(int i=1,x,y;i<=Q;i++) { scanf("%d%d",&x,&y); printf("%d\n",lca(x,y)); } }

bzoj2125 最短路——仙人掌兩點間距離