bzoj2125 最短路——仙人掌两点间距离
题目: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)); } }