bzoj 2125 最短路——仙人掌两点间最短路
题目: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; }