1. 程式人生 > >[BZOJ2125]最短路(圓方樹DP)

[BZOJ2125]最短路(圓方樹DP)

ont int tin ons 最短 bzoj 如果 比較 pri

題意:仙人掌圖最短路。

算法:圓方樹DP,$O(n\log n+Q\log n)$

首先建出仙人掌圓方樹(與點雙圓方樹的區別在於直接連割邊,也就是存在圓圓邊),然後考慮點u-v的最短路徑,顯然就是:在圓方樹上u-v的路徑上的所有邊權之和,加上每個環(方點)中連出去的兩個點的最短距離。

現在問題就是:如何求出環上兩個點的最短路徑。考慮這樣設定邊權,首先顯然圓圓邊的邊權就是原圖的邊權,然後設一個環在搜索樹中深度最小的點為這個環的根,則方圓邊的邊權是環的根到這個點的最短距離,這個可以在Tarjan的時候直接求出。

但是圓方樹問題通常需要在LCA處分圓方點討論。首先如果LCA是圓點,那麽直接做即可。如果是方點,就需要決定要不要走環的另一側,這個同樣直接討論即可。

具體見代碼,感覺思路還是比較清晰的。

 1 #include<cstdio>
 2 #include<algorithm>
 3 #define rep(i,l,r) for (int i=l; i<=r; i++)
 4 using namespace std;
 5 
 6 const int N=20010;
 7 int n,m,Q,u,v,w,tot,tim,top,dep[N],len[N],type[N],stk[N];
 8 int dfn[N],low[N],dis[N],lst[N],fa[N][16],sm[N][16];
9 10 struct E{ 11 int cnt,h[N],to[N<<1],nxt[N<<1],val[N<<1]; 12 void add(int u,int v,int w){ to[++cnt]=v; val[cnt]=w; nxt[cnt]=h[u]; h[u]=cnt; } 13 }G,G1; 14 15 void work(int x,int k){ 16 tot++; int t; len[tot]=dis[stk[top]]-dis[x]+lst[stk[top]]; 17 do{ 18
t=stk[top--]; 19 int A=dis[t]-dis[x],B=len[tot]-A; 20 G1.add(tot,t,min(A,B)); type[t]=(A<=B); 21 }while (t!=k); 22 G1.add(x,tot,0); 23 } 24 25 void Tarjan(int x,int pre){ 26 //printf("%d\n",x); 27 dfn[x]=low[x]=++tim; stk[++top]=x; 28 for (int i=G.h[x],k; i; i=G.nxt[i]){ 29 if ((k=G.to[i])==pre) continue; 30 if (!dfn[k]){ 31 dis[k]=dis[x]+G.val[i]; Tarjan(k,x); 32 //printf("%d %d %d %d\n",x,k,dfn[x],low[k]); 33 if (low[k]>dfn[x]) top--,G1.add(x,k,G.val[i]); 34 else if (low[k]==dfn[x]) work(x,k); 35 low[x]=min(low[x],low[k]); 36 }else low[x]=min(low[x],dfn[k]),lst[x]=G.val[i]; 37 } 38 } 39 40 void dfs(int x,int pre){ 41 for (int i=G1.h[x],k; i; i=G1.nxt[i]) 42 fa[k=G1.to[i]][0]=x,dep[k]=dep[x]+1,sm[k][0]=G1.val[i],dfs(k,x); 43 } 44 45 int lca(int u,int v){ 46 if (dep[u]<dep[v]) swap(u,v); 47 int t=dep[u]-dep[v],res=0; 48 for (int i=15; ~i; i--) if (t&(1<<i)) res+=sm[u][i],u=fa[u][i]; 49 if (u==v) return res; 50 for (int i=15; ~i; i--) if (fa[u][i]!=fa[v][i]) 51 res+=sm[u][i]+sm[v][i],u=fa[u][i],v=fa[v][i]; 52 if (fa[u][0]<=n) return sm[u][0]+sm[v][0]+res; 53 int A=sm[u][0],B=sm[v][0],mn; 54 if (type[u]==type[v]) mn=min(abs(A-B),len[fa[u][0]]-abs(A-B)); 55 else mn=min(A+B,len[fa[u][0]]-A-B); 56 return res+mn; 57 } 58 59 int main(){ 60 freopen("bzoj2125.in","r",stdin); 61 freopen("bzoj2125.out","w",stdout); 62 scanf("%d%d%d",&n,&m,&Q); tot=n; 63 rep(i,1,m) scanf("%d%d%d",&u,&v,&w),G.add(u,v,w),G.add(v,u,w); 64 Tarjan(1,0); dfs(1,0); 65 //rep(i,1,tot) printf("%d ",low[i]); puts(""); 66 rep(j,1,15) rep(i,1,tot) 67 fa[i][j]=fa[fa[i][j-1]][j-1],sm[i][j]=sm[i][j-1]+sm[fa[i][j-1]][j-1]; 68 rep(i,1,Q) scanf("%d%d",&u,&v),printf("%d\n",lca(u,v)); 69 return 0; 70 }

[BZOJ2125]最短路(圓方樹DP)