bzoj2125 最短路

https://www.lydsy.com/JudgeOnline/problem.php?id=2125

http://www.tsinsen.com/ViewGProblem.html?gpid=-1000001268

题解

貌似还有一种仙人掌圆方树,跟点双圆方树区别不大,暂时不清楚有什么本质上的区别(貌似写起来容易?);以下用点双圆方树

建圆方树,当成以dfs起始点(圆点)为根的有根树,方点到其圆点父亲的边权设为0,圆点到其方点父亲的边权设为圆点到其父亲的父亲(是圆点)在原图上的最短距离(可以预处理每个环上的前缀和方便求区间和)(话说我发现自己根本不会维护这个环...研究了一段时间有了一个可行做法:首先tarjan时候确定一个点双时弹出来的边就是点双内边,如果是仙人掌的话,若当前节点是u,可以发现是按照(v,u),(*,v),..,(u,*)的顺序;如果点双内有超过1条边,说明这个点双不是两点一线/单点(然而我并不清楚具体是怎样的?),是环;求出每个环的长度;考虑求出数组s,其中s[i]表示i点所在环的“根”(即环中在圆方树上深度最小的点)在环上顺时针/逆时针(就是一个环要同一个方向)走到i点的距离;割点在多个点双中,怎么办?设该点为u,考虑在其各个子节点(方点)代表的环中,s[u]=0,因此直接定义割点u属于的环是它父亲节点代表的环;查询距离的时候,特判掉割点)

对于询问,如果两点lca是圆点,直接输出两点在树上距离即可

如果两点lca是方点,“题解”里面有讲

错误记录:边数开小了。注意仙人掌的边数范围是[n-1,2*n-2](n是点数)

  1 #include<cstdio>
  2 #include<algorithm>
  3 #include<cstring>
  4 #include<vector>
  5 #include<cassert>
  6 using namespace std;
  7 #define fi first
  8 #define se second
  9 #define mp make_pair
 10 #define pb push_back
 11 typedef long long ll;
 12 typedef unsigned long long ull;
 13 #define int ll
 14 struct E
 15 {
 16     int f,to,nxt,d;
 17 };
 18 int n,m,qq;
 19 int s[20010];//维护从i所在环的"起点"到i的距离
 20 int len[20010];//表示第i个环的长度
 21 namespace T
 22 {
 23 
 24 E e[40100];
 25 int f1[20100],ne=1;
 26 void me(int x,int y,int z)
 27 {
 28     //printf("at%lld %lld %lld\n",x,y,z);
 29     e[++ne].to=y;e[ne].nxt=f1[x];f1[x]=ne;e[ne].f=x;e[ne].d=z;
 30     e[++ne].to=x;e[ne].nxt=f1[y];f1[y]=ne;e[ne].f=y;e[ne].d=z;
 31 }
 32 int anc[20010][16],l2n=15,dd[20010][16],dep[20010];
 33 void dfs(int u,int fa)
 34 {
 35     int i;
 36     anc[u][0]=fa;
 37     for(i=1;i<=l2n;++i)
 38     {
 39         anc[u][i]=anc[anc[u][i-1]][i-1];
 40         dd[u][i]=dd[u][i-1]+dd[anc[u][i-1]][i-1];
 41     }
 42     for(int k=f1[u];k;k=e[k].nxt)
 43         if(e[k].to!=fa)
 44         {
 45             dep[e[k].to]=dep[u]+1;
 46             dd[e[k].to][0]=e[k].d;
 47             dfs(e[k].to,u);
 48         }
 49 }
 50 int getcircledis(int x,int y)//保证x,y在同一个点双内,求两点间距离
 51 //如果满足这个条件,那么要么x,y深度相等,要么一个是另一个的爷爷
 52 {
 53     if(x==y)    return 0;
 54     if(dep[x]!=dep[y])
 55     {
 56         //puts("type1");
 57         if(dep[x]<dep[y])    swap(x,y);
 58         assert(anc[x][1]==y);
 59         return min(s[x],len[anc[x][0]-n]-s[x]);
 60     }
 61     else
 62     {
 63         int an1=s[x]-s[y],an2=s[y]-s[x];
 64         if(an1<0)    an1+=len[anc[x][0]-n];
 65         if(an2<0)    an2+=len[anc[x][0]-n];
 66         return min(an1,an2);
 67     }
 68 }
 69 int calc(int x,int y)
 70 {
 71     if(dep[x]<dep[y])    swap(x,y);
 72     int t=dep[x]-dep[y],an=0,i;
 73     for(i=l2n;i>=0;--i)
 74         if(t&(1<<i))
 75         {
 76             an+=dd[x][i];
 77             x=anc[x][i];
 78         }
 79     if(x==y)    return an;
 80     //printf("1t%lld %lld\n",x,y);
 81     for(i=l2n;i>=0;--i)
 82         if(anc[x][i]!=anc[y][i])
 83         {
 84             an+=dd[x][i];an+=dd[y][i];
 85             //printf("9t%lld %lld\n",dd[x][i],dd[y][i]);
 86             x=anc[x][i];y=anc[y][i];
 87         }
 88     an+=anc[x][0]>n?getcircledis(x,y):(dd[x][0]+dd[y][0]);
 89     return an;
 90 }
 91 
 92 }
 93 namespace G
 94 {
 95 
 96 E e[40100];
 97 int f1[10100],ne=1;
 98 void me(int x,int y,int z)
 99 {
100     e[++ne].to=y;e[ne].nxt=f1[x];f1[x]=ne;e[ne].d=z;e[ne].f=x;
101     e[++ne].to=x;e[ne].nxt=f1[y];f1[y]=ne;e[ne].d=z;e[ne].f=y;
102 }
103 int st[40010];int tp;
104 int dfn[10100],dfc,cnt;//bno[10100],cnt;
105 int tmp[40010],tlen;
106 int dfs(int u,int lst)
107 {
108     int i,k,v,lowu=dfn[u]=++dfc,lowv;
109     for(k=f1[u];k;k=e[k].nxt)
110     {
111         v=e[k].to;
112         if(!dfn[v])
113         {
114             st[++tp]=k;//++chi;
115             lowv=dfs(v,k);lowu=min(lowu,lowv);
116             if(lowv>=dfn[u])
117             {
118                 ++cnt;
119                 tlen=0;
120                 while(st[tp]!=k)    tmp[++tlen]=st[tp--];
121                 tmp[++tlen]=st[tp--];
122                 //printf("1t%lld %lld\n",u,tlen);
123                 int k;
124                 if(tlen>1)
125                 {
126                     s[u]=0;
127                     for(i=1;i<=tlen;++i)
128                     {
129                         k=tmp[i];
130                         s[e[k].f]=s[e[k].to]+e[k].d;
131                     }
132                     len[cnt]=s[u];
133                     for(i=1;i<tlen;++i)
134                     {
135                         k=tmp[i];
136                         T::me(n+cnt,e[k].f,min(s[e[k].f],
137                             len[cnt]-s[e[k].f]));
138                     }
139                     T::me(n+cnt,u,0);
140                 }
141                 else
142                 {
143                     k=tmp[1];
144                     s[e[k].to]=e[k].d;
145                     len[cnt]=2*s[e[k].to];
146                     T::me(n+cnt,u,e[k].d);
147                     T::me(n+cnt,e[k].to,0);
148                 }
149             }
150         }
151         else if(dfn[v]<dfn[u]&&k!=(lst^1))
152         {
153             st[++tp]=k;
154             lowu=min(lowu,dfn[v]);
155         }
156     }
157     return lowu;
158 }
159 void work()
160 {
161     int i;
162     dfs(1,0);
163     T::dfs(1,0);
164 }
165 
166 }
167 signed main()
168 {
169     int i,x,y,z;
170     scanf("%lld%lld%lld",&n,&m,&qq);
171     for(i=1;i<=m;++i)
172     {
173         scanf("%lld%lld%lld",&x,&y,&z);
174         G::me(x,y,z);
175     }
176     G::work();
177     //printf("at%lld\n",G::tp);
178     /*
179     for(i=1;i<=n;++i)
180         printf("1t%lld\n",s[i]);
181     for(i=1;i<=G::cnt;++i)
182         printf("2t%lld\n",len[i]);
183     while(1)
184     {
185         int a,b;
186         scanf("%lld%lld",&a,&b);
187         printf("%lld\n",T::getcircledis(a,b));
188     }
189     */
190     while(qq--)
191     {
192         scanf("%lld%lld",&x,&y);
193         printf("%lld\n",T::calc(x,y));
194     }
195     return 0;
196 }
View Code

数据生成器(只能生成每个点、边都仅在一个环上的仙人掌)

from random import *
circle_num=50
n0=0
cir=[[] for i in range(10000)]
cirlen=[0]*1000
e=[]
ld=1
rd=500
for i in range(circle_num):
    cirlen[i]=randint(1,50)
    for j in range(cirlen[i]):
        n0+=1
        cir[i].append(n0)
    #print('t1',n0)
    for j in range(cirlen[i]-1):
        e.append([cir[i][j],cir[i][j+1],randint(ld,rd)])
    e.append([cir[i][0],cir[i][cirlen[i]-1],randint(ld,rd)])
    #print(e)
    #print(cir)
for i in range(1,circle_num):
    fa=randint(0,i-1)
    e.append([cir[fa][randint(0,cirlen[fa]-1)],cir[i][randint(0,cirlen[i]-1)],randint(ld,rd)])
qq=5000
print(n0,len(e),qq)
for i in e:
    print(i[0],i[1],i[2])
for i in range(qq):
    print(randint(1,n0),randint(1,n0))
View Code

 

posted @ 2018-10-30 16:33  hehe_54321  阅读(146)  评论(0编辑  收藏  举报
AmazingCounters.com