最近屯题都忘了把解题报告写上了
这道题是一道比较烦的LCT,我们先考虑每个点上到底要维护什么
我们设路径上有n个点,从起点到终点编号为1~n
显然期望=S/[(n+1)n div 2]
S=∑a[i]*i*(n-i+1) //这里理解一下
=∑a[i]*[i*(n+1)-i*i]=(n+1)*∑a[i]*i-∑a[i]*i*i;
考虑到找出x,y之间的路径就是把x,y路径上的点弄到一棵Auxiliary tree上
对于每个点x,我们显然要维护以x为根的子树对应的序列的∑a[i]*i和∑a[i]*i*i,记为s2[x],s3[x]
对于上面表达式的i,就是每个点在树(子树)上的名次
下面的关键就是我们如何用其左右孩子的信息来维护父亲x,设l为x左孩子,r为x右孩子
这里主要的影响在于对右孩子,原来右孩子的维护的是编号为1~size[r]的序列的s2[],s3[]
现在我们加上去的是编号为size[l]+2~size[x]序列的s2[],s3[]
我们考虑会会增加多少,对于s2 ∑a[i]*(i+p)-∑a[i]*i=p*∑a[i]
对于s3 有∑a[i]*(i+p)^2-∑a[i]*i^2=∑a[i]*(2*p*i+p*p)=p*p*∑a[i]+2*p*∑a[i]*i
这里维护就很显然了,我们还要再维护一个s1[]记录∑a[i]
而修改相对比较简单,其实就是对s1,s2,s3分别加上d*n,d*∑i,d*∑i*i 这个直接上公式
但这里还并没有完,我们用的LCT有个操作叫换根,而换根要翻转左右子树
而这样左右子树的编号就反过来了,从而节点上记录s2[],s3[]就不正确了
我的解决方法是再维护ss2[],ss3[],记录倒序编号时∑a[i]*i和∑a[i]*i*i
然后要左右子树交换时,就交换ss2[],s2[]和ss3[]和s3[]
具体实现细节见程序
1 type node=record 2 po,next:longint; 3 end; 4 5 var son:array[0..50010,1..2] of longint; 6 p,a,q,fa,add,size:array[0..50010] of longint; 7 s1,ss2,ss3,s2,s3:array[0..50010] of int64; 8 rev:array[0..50010] of boolean; 9 e:array[0..100010] of node; 10 cc,t,len,ch,n,m,x,y,z,i:longint; 11 12 function gcd(a,b:int64):int64; 13 begin 14 if b=0 then exit(a) 15 else exit(gcd(b,a mod b)); 16 end; 17 18 procedure build(x,y:longint); 19 begin 20 inc(len); 21 e[len].po:=y; 22 e[len].next:=p[x]; 23 p[x]:=len; 24 end; 25 26 procedure dfs(x:longint); 27 var i,y:longint; 28 begin 29 i:=p[x]; 30 while i<>0 do 31 begin 32 y:=e[i].po; 33 if fa[x]<>y then 34 begin 35 fa[y]:=x; 36 dfs(y); 37 end; 38 i:=e[i].next; 39 end; 40 end; 41 42 procedure swap(var a,b:int64); 43 var c:int64; 44 begin 45 c:=a; 46 a:=b; 47 b:=c; 48 end; 49 50 procedure reverse(x:longint); //如上述 51 var p:longint; 52 begin 53 swap(ss2[x],s2[x]); 54 swap(ss3[x],s3[x]); 55 rev[x]:=not rev[x]; 56 end; 57 58 function root(x:longint):boolean; 59 begin 60 exit((son[fa[x],1]<>x) and (son[fa[x],2]<>x)); 61 end; 62 63 procedure calc(x,z:longint); 64 var p,c,d:int64; 65 begin 66 inc(add[x],z); 67 inc(a[x],z); 68 p:=size[x]; 69 c:=int64(z)*(p+1)*p div 2; 70 d:=int64(z)*(p+1)*p*(2*p+1) div 6; //平方和公式 71 s1[x]:=s1[x]+int64(z)*p; 72 s2[x]:=s2[x]+c; 73 s3[x]:=s3[x]+d; 74 ss2[x]:=ss2[x]+c; 75 ss3[x]:=ss3[x]+d; 76 end; 77 78 procedure update(x:longint); 79 var l,r:longint; 80 p,q:int64; 81 begin 82 l:=son[x,1]; 83 r:=son[x,2]; 84 size[x]:=size[l]+size[r]+1; 85 p:=size[l]+1; //正序编号 86 q:=size[r]+1; //反序标号 87 s1[x]:=s1[l]+s1[r]+a[x]; 88 s2[x]:=s2[l]+int64(a[x])*p+s2[r]+s1[r]*p; 89 s3[x]:=s3[l]+int64(a[x])*p*p+s3[r]+p*p*s1[r]+2*p*s2[r]; 90 ss2[x]:=ss2[r]+int64(a[x])*q+ss2[l]+s1[l]*q; 91 ss3[x]:=ss3[r]+int64(a[x])*q*q+ss3[l]+q*q*s1[l]+2*q*ss2[l]; 92 end; 93 94 procedure push(x:longint); 95 var r:longint; 96 begin 97 if rev[x] then 98 begin 99 r:=son[x,1]; son[x,1]:=son[x,2]; son[x,2]:=r; 100 if son[x,1]<>0 then reverse(son[x,1]); 101 if son[x,2]<>0 then reverse(son[x,2]); 102 rev[x]:=false; 103 end; 104 if add[x]<>0 then 105 begin 106 if son[x,1]<>0 then calc(son[x,1],add[x]); 107 if son[x,2]<>0 then calc(son[x,2],add[x]); 108 add[x]:=0; 109 end; 110 end; 111 112 procedure rotate(x,w:longint); 113 var y:longint; 114 begin 115 y:=fa[x]; 116 if not root(y) then 117 begin 118 if son[fa[y],1]=y then son[fa[y],1]:=x 119 else son[fa[y],2]:=x; 120 end; 121 fa[x]:=fa[y]; 122 son[y,3-w]:=son[x,w]; 123 fa[son[x,w]]:=y; 124 son[x,w]:=y; 125 fa[y]:=x; 126 update(y); 127 end; 128 129 procedure splay(x:longint); 130 var i,y:longint; 131 fl:boolean; 132 begin 133 t:=0; 134 i:=x; 135 while not root(i) do 136 begin 137 inc(t); 138 q[t]:=i; 139 i:=fa[i]; 140 end; 141 inc(t); 142 q[t]:=i; 143 for i:=t downto 1 do 144 push(q[i]); 145 if t=1 then exit; 146 fl:=true; 147 while fl do 148 begin 149 y:=fa[x]; 150 if y=q[t] then 151 begin 152 if son[y,1]=x then rotate(x,2) 153 else rotate(x,1); 154 fl:=false; 155 end 156 else begin 157 if fa[y]=q[t] then fl:=false; 158 if son[fa[y],1]=y then 159 begin 160 if son[y,1]=x then rotate(y,2) 161 else rotate(x,1); 162 rotate(x,2); 163 end 164 else begin 165 if son[y,1]=x then rotate(x,2) 166 else rotate(y,1); 167 rotate(x,1); 168 end; 169 end; 170 end; 171 update(x); 172 end; 173 174 procedure access(x:longint); 175 var y:longint; 176 begin 177 y:=0; 178 repeat 179 splay(x); 180 son[x,2]:=y; 181 update(x); 182 y:=x; 183 x:=fa[x]; 184 until x=0; 185 end; 186 187 function find(x,y:longint):boolean; 188 begin 189 while son[y,1]<>0 do y:=son[y,1]; //Auxiliary tree最左的点即为所在树的根节点 190 if y=x then exit(true) else exit(false); 191 end; 192 193 procedure makeroot(x:longint); 194 begin 195 access(x); 196 splay(x); 197 reverse(x); //注意,在这WA了几次 198 end; 199 200 procedure path(x,y:longint); 201 begin 202 makeroot(x); 203 access(y); 204 splay(y); 205 end; 206 207 procedure link(x,y:longint); 208 begin 209 if cc=0 then exit; //一点小优化,cc记录删掉的边数 210 path(x,y); 211 if not find(x,y) then 212 begin 213 fa[x]:=y; 214 dec(cc); 215 end; 216 end; 217 218 procedure cut(x,y:longint); 219 begin 220 path(x,y); 221 if (son[y,1]=x) and (son[x,2]=0) then //注意两点间有边的判定 222 begin 223 inc(cc); 224 fa[x]:=0; 225 son[y,1]:=0; 226 end; 227 end; 228 229 procedure change(x,y,z:longint); 230 begin 231 path(x,y); 232 if (cc=0) or find(x,y) then calc(y,z); 233 end; 234 235 procedure ask(x,y:longint); 236 var a,b,c:int64; 237 begin 238 path(x,y); 239 if (cc=0) or find(x,y) then 240 begin 241 c:=size[y]; 242 a:=(c+1)*s2[y]-s3[y]; 243 b:=c*(c+1) div 2; 244 c:=gcd(a,b); 245 writeln(a div c,'/',b div c); 246 end 247 else writeln(-1); 248 end; 249 250 begin 251 readln(n,m); 252 for i:=1 to n do 253 begin 254 read(a[i]); 255 size[i]:=1; 256 end; 257 for i:=1 to n-1 do 258 begin 259 readln(x,y); 260 build(x,y); 261 build(y,x); 262 end; 263 dfs(1); //这里先dfs构造树,如果直接link的话会TLE目测 264 for i:=1 to m do 265 begin 266 read(ch,x,y); 267 if ch=1 then 268 cut(x,y) 269 else if ch=2 then 270 link(x,y) 271 else if ch=4 then 272 ask(x,y) 273 else begin 274 read(z); 275 change(x,y,z); 276 end; 277 readln; 278 end; 279 end.