• [BZOJ2125]最短路(圆方树DP)


    题意:仙人掌图最短路。

    算法:圆方树DP,$O(nlog n+Qlog 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
    ",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
    ",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
    ",lca(u,v));
    69     return 0;
    70 }
  • 相关阅读:
    使用Android自定义格式的定义Button
    Java Binary Search
    非常成功的人会做的八件事
    Ubuntu12.04 安装java
    NetBeans 时事通讯(刊号 # 125 Nov 17, 2010)
    NetBeans IDE 7.0 Beta 发布
    关于 IPv6 你需要知道的 10 件事
    开始学习 Go
    开始学习 Go
    Quartz 1.8.4 发布
  • 原文地址:https://www.cnblogs.com/HocRiser/p/9143979.html
Copyright © 2020-2023  润新知