1. 程式人生 > >bzoj 2125 最短路——仙人掌兩點間最短路

bzoj 2125 最短路——仙人掌兩點間最短路

printf 細節 col ren php ace std 最短路 i++

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

因為看了TJ又抄了標程,現在感覺還是輕飄飄的……必須再做一遍。

兩點間的情況:

1.直到 lca 都沒有在一個環上的部分;

2.本來就處在一個環上;

3.本來不在一個環上,快到 lca 的時候開始處在一個環上了。

第一種情況就普通弄就行。處理倍增 lca 數組和根到每個點的最短路dis值。

第二種情況在環上兩部分取較短的就行。

第三種情況是前兩種的結合。需要找到 p 和 q 剛開始在同一個環上時的那兩個進入點 x 和 y。然後dis[ p ] - dis[ x ] + dis[ q ] - dis[ y ]再加上 p、q 環上兩部分較短的。

怎麽取較短的呢?

可以弄dfs序的距離。

實現的時候第二種和第三種情況可以合並。

細節有一些不明白:環的最高點的深度和環上別的點不一樣,也行嗎?還有邊的數組大小怎麽算?

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int N=1e4+5;
int n,m,q,hd[N],xnt=1,g[N],len[N],cnt,dep[N],f[N][20];
int dis[N],ds[N],fa[N],dfn[N],tim;
bool vis[N]; struct Ed{ int nxt,to,w;bool del; Ed(int n=0,int t=0,int w=0):nxt(n),to(t),w(w) {del=0;} }ed[N<<3]; int tabs(int k){return k<0?-k:k;} void add(int x,int y,int z) { ed[++xnt]=Ed(hd[x],y,z);hd[x]=xnt; } void spfa() { memset(dis,0x3f,sizeof dis);dis[1]=0; queue<int> q;q.push(1
);vis[1]=1; while(q.size()) { int k=q.front();q.pop();vis[k]=0;//don‘t forget visk=0!! for(int i=hd[k],v;i;i=ed[i].nxt) if(dis[v=ed[i].to]>dis[k]+ed[i].w) { dis[v]=dis[k]+ed[i].w; if(!vis[v])vis[v]=1,q.push(v); } } } void circle(int y,int e) { int x=ed[e].to;//! len[++cnt]=ed[e].w;g[y]=cnt; ed[e].del=ed[e^1].del=1;//don‘t forget del! add(x,y,0);add(y,x,0);// edw has no limit--only for bfs the dep // edw is guaranteed by ds[] for(e=fa[y];(y=ed[e^1].to)!=x;e=fa[y]) { len[cnt]+=ed[e].w;g[y]=cnt;ed[e].del=ed[e^1].del=1; add(x,y,0);add(y,x,0); } len[cnt]+=ed[e].w;g[x]=cnt; } void dfs(int cr) { dfn[cr]=++tim; for(int i=hd[cr],v;i;i=ed[i].nxt) if(!dfn[v=ed[i].to]) { fa[v]=i;ds[v]=ds[cr]+ed[i].w;dfs(v); } else if(i!=(fa[cr]^1)&&dfn[v]<dfn[cr])circle(cr,i);//cr!!! } void bfs() { queue<int> q;q.push(1);dep[1]=1; while(q.size()) { int k=q.front();q.pop(); for(int i=hd[k],v;i;i=ed[i].nxt) if(!ed[i].del&&!dep[v=ed[i].to]) { dep[v]=dep[k]+1;q.push(v); f[v][0]=k; for(int j=1;j<=15;j++)//j not i { f[v][j]=f[f[v][j-1]][j-1]; } } } } int lca(int x,int y) { if(dep[x]<dep[y])swap(x,y); int p=x,q=y; for(int i=15;i>=0;i--) if(dep[f[x][i]]>=dep[y])x=f[x][i]; if(x==y)return dis[p]-dis[q]; for(int i=15;i>=0;i--) if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i]; if(g[x]!=g[y]||(!g[x]&&!g[y]))return dis[p]+dis[q]-2*dis[f[x][0]];//&& !g[x]&&!g[y]!!! int k=tabs(ds[x]-ds[y]); return dis[p]-dis[x]+dis[q]-dis[y]+min(k,len[g[x]]-k); // include p&q in different circle but x&y in the same circle; //because the dep of nodes in the same circle are almost the same } int main() { scanf("%d%d%d",&n,&m,&q);int x,y,z; for(int i=1;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;i<=q;i++) { scanf("%d%d",&x,&y); printf("%d\n",lca(x,y)); } return 0; }

bzoj 2125 最短路——仙人掌兩點間最短路