动态修改询问树上两点距离 poj2763

acm归来

应该说又是一次铩羽,发现其他高中的队伍普遍在我们前面,雅礼包揽了前2,我们高三神牛只拿了第6,然后就是我们uracil排到了22位,主要是c题依poj的习惯打了not seekeof

结果测评器不认账,死活报wa,然后就是g题,题目数据范围有问题,结果不告诉我们,足足花了2个半小时,在2:30即将下考时才ac,但是本来可以再a一道动规的,结果没时间不说,罚时罚的想吐,排在了sol 6的最后一名。

poj 2763应该可以算是动态树问题,但是我用欧拉序列+线段树给过了(其实我发现网上普遍也是用这种算法)

给你n个节点的带权树,m个操作,当前位置为s

每个操作:0 x 从当前节点移动到x,并输出距离

                    1 x y 修改第x条边权值为y

dist=dist[s]+dist[x]-2*dist[lca(s,x)];dist指到根的距离。

我们构造一个欧拉序列(使每棵子树的开头和末尾均为根),当某条边权值被改变,即在这棵子树中每个节点dist值均+(w‘-w),因此我们用线段树维护,每次标记永久化(或加时间戳)进行区间修改,求dist回收标记即可。

lca tarjan,rmq均可。

var tail:array[1..100000]of longint;
    next,sora,cost:array[1..500000]of longint;
    root,l,r,pp,pos,pos2,d,ww:array[0..100000]of longint;
    di:array[1..524288]of longint;
    f:array[1..500000,0..20]of longint;
    o:array[0..20]of longint;
    len,n,m,m1,ss,dep,ll:longint;
function min(x,y:longint):longint;
begin
 if d[x]<d[y] then exit(x) else exit(y)
end;
procedure dfs(x,y,w:longint);
var i,ne:longint;
begin
 root[x]:=y;
 inc(dep);inc(ll);pp[x]:=ll;d[x]:=dep;f[ll,0]:=x;
 inc(len);di[len+m1]:=w;l[x]:=len;
 i:=x;
 while next[i]<>0 do begin
  i:=next[i];ne:=sora[i];
  if ne<>y then dfs(ne,x,w+cost[i]);
  inc(ll);f[ll,0]:=x
 end;
 inc(len);di[len+m1]:=w;r[x]:=len
end;
function dis(x:longint):longint;
begin
 x:=x+m1;dis:=0;
 while x<>0 do begin
  dis:=dis+di[x];
  x:=x>>1
 end
end;
procedure note(l,r,w:longint);
begin
 l:=l+m1-1;r:=r+m1+1;
 while not(l xor r=1) do begin
  if l and 1=0 then di[l+1]:=di[l+1]+w;
  if r and 1=1 then di[r-1]:=di[r-1]+w;
  l:=l>>1;r:=r>>1
 end
end;
procedure origin;
var i:longint;
begin
 m1:=1;
 while m1<n<<1+2 do m1:=m1<<1;m1:=m1-1;
 for i:=1 to n do tail[i]:=i;ss:=n;
end;
procedure link(x,y,z:longint);
begin
 inc(ss);next[tail[x]]:=ss;tail[x]:=ss;sora[ss]:=y;cost[ss]:=z;
 inc(ss);next[tail[y]]:=ss;tail[y]:=ss;sora[ss]:=x;cost[ss]:=z
end;
procedure rmq;
var i,j,k:longint;
begin
 o[0]:=1;
 for i:=1 to 20 do o[i]:=o[i-1]<<1;
 k:=trunc(ln(ll)/ln(2));
 for j:=1 to k do
  for i:=1 to ll do
   if i+o[j]-1<=ll then f[i,j]:=min(f[i,j-1],f[i+o[j-1],j-1])
    else break
end;
procedure init;
var i,x,y,z,ch,s,lll,rrr,lca,ans,w,ans1,ans2,ans3,k:longint;
begin
 readln(n,m,s);
 origin;
 for i:=1 to n-1 do begin
  readln(x,y,z);pos[i]:=y;pos2[i]:=x;ww[i]:=z;
  link(x,y,z)
 end;
 d[0]:=maxlongint;dep:=0;ll:=0;
 dfs(s,0,0);
 rmq;
 for i:=1 to m do begin
  read(ch);
  if ch=0 then begin
   readln(x);
   if pp[x]<pp[s] then begin lll:=pp[x];rrr:=pp[s] end else begin lll:=pp[s];rrr:=pp[x] end;
   k:=trunc(ln(rrr-lll+1)/ln(2));
   lca:=min(f[lll,k],f[rrr-o[k]+1,k]);
   ans1:=dis(l[x]);ans2:=dis(l[s]);ans3:=dis(l[lca]);
   ans:=ans1+ans2-2*ans3;
   writeln(ans);
   s:=x
  end
  else begin
   readln(x,w);
   if root[pos[x]]=pos2[x] then lll:=pos[x] else lll:=pos2[x];
   note(l[lll],r[lll],w-ww[x]);
   ww[x]:=w
  end
 end;
end;
begin
assign(input,'2763.in');reset(input);
assign(output,'2763.out');rewrite(output);
 init;
close(input);close(output)
end.


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值