原题网址:http://codeforces.com/contest/697/problem/C
对于一个修改(u,v),值为w的路径,可以拆成3部分,u到根+w,v到根+w,lca(u,v)到根-w*2。每次求值也拆成3部分,将u到根,v到根,lca(u,v)到根的三条路径分别和前面记下来的付费路径扫一遍,答案就是calc(u)+calc(v)-calc(lca(u,v))*2。
这样做的优雅之处在于每次只需求两条到根路径的交,不需要分类讨论,而且由于树的特殊性,可以用位运算优化。
如果每次求lca都暴力一层一层往上跑的话,复杂度是O(q^2*lg(10^8)),看上去可能会T,但cf上交得过。
type
point=record
x,w:int64;
end;
var
a:array[0..10000] of point;
opt,u,v,w,q,s:int64;
l:extended;
function lca(x,y:int64):int64;
var
e:int64;
begin
e:=1;
while x<>y do
if x>y
then x:=x>>e
else y:=y>>e;
exit(x);
end;
procedure ins(x,w:int64);
begin
inc(s);
a[s].x:=x;
a[s].w:=w;
end;
function calc(x:int64):int64;
var
ans,sum,l:int64;
i:longint;
begin
ans:=0;
for i:=1 to s do
begin
sum:=0;l:=lca(x,a[i].x);
while l>0 do
begin
l:=l>>1;
inc(sum);
end;
inc(ans,sum*a[i].w);
end;
exit(ans);
end;
begin
readln(q);
s:=0;l:=ln(2);
repeat
read(opt);dec(q);
if opt=1
then begin read(u,v,w);ins(u,w);ins(v,w);ins(lca(u,v),-w*2);end
else begin read(u,v);writeln(calc(u)+calc(v)-calc(lca(u,v))*2);end;
until q=0;
end.
但一开始我是想用位运算优化成O(q^2),也就是O(1)算出lca,但Pascal的ln函数估计是炸精度了,一直WA。下面这个程序如果不考虑精度是理论能A的,而且复杂度是O(q^2)。
type
point=record
x,w:int64;
end;
var
a:array[0..10000] of point;
opt,u,v,w,q,s:int64;
function lca(x,y:int64):int64;
var
t:int64;
begin
if x>y then begin t:=x;x:=y;y:=t;end;
y:=y>>int64(trunc(ln(y)/ln(2))-trunc(ln(x)/ln(2)));
if x xor y=int64(0)
then t:=int64(0)
else t:=int64(trunc(ln(x xor y)/ln(2))+1);
x:=x>>t;
y:=y>>t;
exit(x);
end;
procedure ins(x,w:int64);
begin
inc(s);
a[s].x:=x;
a[s].w:=w;
end;
function calc(x:int64):int64;
var
ans,sum:int64;
i:longint;
begin
ans:=0;
for i:=1 to s do
begin
sum:=trunc(ln(lca(x,a[i].x))/ln(2));
inc(ans,sum*a[i].w);
end;
exit(ans);
end;
begin
readln(q);
s:=0;
repeat
read(opt);dec(q);
if opt=1
then begin read(u,v,w);ins(u,w);ins(v,w);ins(lca(u,v),-w*2);end
else begin read(u,v);writeln(calc(u)+calc(v)-calc(lca(u,v))*2);end;
until q=0;
end.