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 }
数据生成器(只能生成每个点、边都仅在一个环上的仙人掌)
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))