Codeforces Round #362 (Div. 2) C Lorenzo Von Matterhorn

原题网址: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.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值