BZOJ2125 最短路
每个点有两个值,一个是从根到这个点的最短路d[i],一个是从根沿dfs树到这个点的距离rd[i].
之后是一个很牛逼的建图,把环上的点都连到环中深度最浅的点得到一颗树,并维护每个点所在的环以及每个环的环长。
对于一个询问(x,y),假设dep[x]>dep[y],分情况:
1.如果x和y的lca是y,那么答案为d[x]-d[y].
2.如果x和y的lca没在环里,那么答案为d[x]+d[y]-2*d[lca].
3.如果x和y的lca在环里,设x,y向上走遇到的第一个环上节点分别是a,b,那么答案为d[x]-d[a]+d[y]-d[b]+min(abs(rd[a]-rd[b]),环长-abs(rd[a]-rd[b])).
#include <cstdio> #include <cstring> #include <queue> #include <algorithm> using namespace std; const int N=10005,M=100005; int n,m,q,e=1,x,y,z,ti,cn,hd[N],nx[M],to[M],w[M],dl[M],dfn[N],st[N],d[N],v[N],rd[N],dp[N],bl[N],ln[N],f[N][14]; void ad(int x,int y,int z) {to[++e]=y,w[e]=z,nx[e]=hd[x],hd[x]=e;} void spfa() { memset(d,0x3f,sizeof d); queue<int> q; q.push(1),d[1]=0; while(!q.empty()) { int u=q.front(); q.pop(); v[u]=0; for(int i=hd[u];i;i=nx[i]) if(d[to[i]]>d[u]+w[i]) { d[to[i]]=d[u]+w[i]; if(!v[to[i]]) v[to[i]]=1,q.push(to[i]); } } } void gt(int x,int y) { if(x==y) return; bl[x]=cn,ad(y,x,0),dl[st[x]]=dl[st[x]^1]=1,ln[cn]+=w[st[x]],gt(to[st[x]^1],y); } void dfs(int x) { dfn[x]=++ti; for(int i=hd[x];i;i=nx[i]) if(i!=(st[x]^1)&&i<=m*2+1) { if(!dfn[to[i]]) rd[to[i]]=rd[x]+w[i],st[to[i]]=i,dfs(to[i]); else if(dfn[to[i]]<dfn[x]) ln[++cn]=w[i],gt(x,to[i]); } } void dfs2(int x) { for(int i=hd[x];i;i=nx[i]) if(!dl[i]&&!dp[to[i]]) f[to[i]][0]=x,dp[to[i]]=dp[x]+1,dfs2(to[i]); } int qry(int x,int y) { if(dp[x]<dp[y]) swap(x,y); int a=x,b=y; for(int i=13;~i;i--) if(dp[f[x][i]]>=dp[y]) x=f[x][i]; if(x==y) return d[a]-d[b]; for(int i=13;~i;i--) if(f[x][i]^f[y][i]) x=f[x][i],y=f[y][i]; if(bl[x]&&bl[x]==bl[y]) { int r=abs(rd[x]-rd[y]); return d[a]-d[x]+d[b]-d[y]+min(r,ln[bl[x]]-r); } return d[a]+d[b]-2*d[f[x][0]]; } int main() { scanf("%d%d%d",&n,&m,&q); for(int i=1;i<=m;i++) scanf("%d%d%d",&x,&y,&z),ad(x,y,z),ad(y,x,z); spfa(),dfs(1),dp[1]=1,dfs2(1); for(int j=1;j<14;j++) for(int i=1;i<=n;i++) f[i][j]=f[f[i][j-1]][j-1]; while(q--) scanf("%d%d",&x,&y),printf("%d\n",qry(x,y)); return 0; }