bzoj3091

最近屯题都忘了把解题报告写上了
这道题是一道比较烦的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.
View Code
原文地址:https://www.cnblogs.com/phile/p/4472987.html