【BZOJ2125最短路】初识圆方树
sto sto sto %%%yyb%%% orz orz orz
不知道圆方树应该归在数据结构还是图论,,先放在这里,,
圆方树,对于狭义圆方树,可以解决一类仙人掌的问题。对于仙人掌的定义,通俗来讲就是对于一个无向图每一条边有且仅仅被一个环所包含(最后看起来就像是一个一个环圈圈,最终just like一个仙人掌。(网上有很多仙人掌的图片orz)
圆方树的结点分为方点和圆点,圆点就是原图上的点,方点是我们新构建出来的点,对于一张原图,有几个环(点双连通分量,因此圆方树也被称为点双树),就有几个方点。对于新图,所有环上的点都与这个环对应的方点相连,而对于不在同一个环上连接圆点的边,我们在新图中直接相连。因此,最终图的样子看起来就像一朵一朵的菊花相连(雾)。
对于方点与方点之间不会有边相连,即只存在圆点与方点的边和圆点和圆点之间的边。最后我们构造出来的新图一定是一棵无根树(显而易见)。
发现点双连通分量,我们可以想到tarjan,对,我们利用tarjan一边就将新图建出来了,同时我们对于圆点到圆点边的距离就是就是原图边的距离,对于圆点到方点的距离定义为这个点到环上在tarjan的dfs树上的深度最浅的那个点的最短距离,同时记录这个最短距离是不是经过dfs树上祖先树上来的还是返祖边来的,同时将每个环的环长记录下来。
那么我们转回到这道题上来。这道题找的是在仙人掌上求两点之间的最短路。那么我们想到建立圆方树后,我们对于普通的树上两点求距离就是dis[x]+dis[y]-2dis[lca],同理圆方树我们用树剖用倍增求LCA都是可以的。如果对于两点,他们的LCA在圆点上,那么就是dis[x]+dis[y]-2dis[lca]如果他们的LCA在方点上,说明他们之间的路径一定是经过了环且这个环就是这个方点对应的环。
那么我们利用树剖跳跳跳,或者利用倍增 跳跳跳,使得他们跳到同LCA的一个环上。对于一个环上的两个点在环上的最短距离即他们可以走两条路,我们利用dis和是否经过返祖边最终可以很好搞出在同一个环上的两点之间的距离。
具体在同一个环上两点x,y距离:我们将x和y的dis[x]和dis[y]利用之间的记录和环的长度,得到一定走返祖边的距离,两个相减取abs得其中一条路距离,最终min(环长-一条路距,一条路距)
于是就这么搞出来啦!
/* tarjan建圆方树,圆点到方点的距离表示到树上深度最浅的点的距离 (同时记录搞这个距离是否经过返祖边 树剖,如果LCA在圆点上则与树上最短距离相同 LCA在方点上表示这两个点的祖先有两个(在一个仙人掌上) 先跳到同一个仙人掌上,然后乱搞就好了 */ #include<stdio.h> #include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<cstring> using namespace std; const int maxn = 200005; const int maxm = 500005; int n,m,q; int tot; struct line{int en,nt,len;}; struct tu { line lin[maxm]; int la[maxn]; int cnt; void addedge(int a,int b,int c) { lin[++cnt]=(line){b,la[a],c}; la[a]=cnt; lin[++cnt]=(line){a,la[b],c}; la[b]=cnt; } }G,V; struct YFS { int newid[maxn],top[maxn],zerz[maxn],fa[maxn],siz[maxn],dis[maxn]; int dfx,dep[maxn],oldid[maxn],cir[maxn]; bool fz[maxn]; void dfs1(int x,int ba) { fa[x] = ba; dep[x] = dep[ba] + 1; siz[x] = 1; for(int it=G.la[x];it;it=G.lin[it].nt) { if(G.lin[it].en==fa[x]) continue; dis[G.lin[it].en]=dis[x]+G.lin[it].len; dfs1(G.lin[it].en,x); siz[x]+=siz[G.lin[it].en]; if(siz[G.lin[it].en]>siz[zerz[x]]) zerz[x]=G.lin[it].en; } } void dfs2(int x,int ace) { newid[x]=++dfx; top[x]=ace; oldid[dfx]=x; if(zerz[x]) dfs2(zerz[x],ace); for(int it=G.la[x];it;it=G.lin[it].nt) { int to=G.lin[it].en; if(to==fa[x]||to==zerz[x]) continue; dfs2(to,to); } } int getlca(int x,int y) { while(top[x]^top[y]) dep[top[x]]<dep[top[y]]?y=fa[top[y]]:x=fa[top[x]]; return dep[x]<dep[y]?x:y; } int tiao(int x,int lca) { int tt; while(top[x]!=top[lca]) { tt = top[x],x=fa[top[x]]; } return x==lca?tt:oldid[newid[lca]+1]; } int query(int x,int y) { int lca = getlca(x,y); if(lca<=n) return dis[x]+dis[y]-2*dis[lca]; int xx=tiao(x,lca); int yy = tiao(y,lca); int dx=dis[xx]-dis[lca],dy=dis[yy]-dis[lca]; if(!fz[xx]) dx = cir[lca]-dx; if(!fz[yy]) dy = cir[lca]-dy; return dis[x]-dis[xx]+dis[y]-dis[yy]+min(abs(dx-dy),cir[lca]-abs(dx-dy)); } }z; int low[maxn],dfn[maxn],dfx,dep[maxn],dis[maxn]; int sta[maxn]; int ba[maxn]; void makeYF(int x,int y,int didi) { int topp=dep[y]-dep[x]+1; ++tot; for(int i=y;i!=x;i=ba[i]) sta[topp--]=i; sta[1] = x; int cd = dep[y]-dep[x]+1; int sum = dis[y]-dis[x]+didi; z.cir[tot] = sum; int jl = 0; for(int i=1;i<=cd;i++) { didi = min(jl,sum-jl); G.addedge(tot,sta[i],didi); z.fz[sta[i]] = (didi==jl); jl+=(-dis[sta[i]]+dis[sta[i+1]]); } } void tarjan(int x,int fa) { low[x]=dfn[x]=++dfx; ba[x]=fa; dep[x]=dep[fa]+1; for(int it=V.la[x];it;it=V.lin[it].nt) { int to = V.lin[it].en; if(fa==to) continue; if(!dfn[to]) { dis[to] = dis[x]+V.lin[it].len; tarjan(to,x); low[x]=min(low[x],low[to]); } else low[x]=min(low[x],dfn[to]); if(dfn[x]<low[to]) G.addedge(x,to,V.lin[it].len); } for(int it=V.la[x];it;it=V.lin[it].nt) { int to = V.lin[it].en; if(dfn[to]>dfn[x]&&ba[to]!=x) makeYF(x,to,V.lin[it].len); } } int main() { scanf("%d%d%d",&n,&m,&q); tot = n; for(int i=1;i<=m;i++) { int a,b,c; scanf("%d%d%d",&a,&b,&c); V.addedge(a,b,c); } tarjan(1,0); z.dfs1(1,0); z.dfs2(1,1); for(int i=1;i<=q;i++) { int a,b; scanf("%d%d",&a,&b); printf("%d\n",z.query(a,b)); } }