国家集训队 树(罗雨屏)(线段树+2K父亲+dfs序)

中国国家队训练
问题描述
  给定一棵大小为 n 的有根点权树,支持以下操作:
  • 换根
  • 修改点权


  • 查询子树最小值

Input

  第一行两个整数 n, Q ,分别表示树的大小和操作数。
  接下来n行,每行两个整数f,v,第i+1行的两个数表示点i的父亲和点i的权。保证f < i。如 果f = 0,那么i为根。输入数据保证只有i = 1时,f = 0。
  接下来 m 行,为以下格式中的一种:
  • V x y表示把点x的权改为y
  • E x 表示把有根树的根改为点 x
  • Q x 表示查询点 x 的子树最小值

     最开始根为1。

Output

  对于每个 Q ,输出子树最小值。  

数据规模和约定
  对于 10% 的数据:n, Q ≤ 5;
  对于其余 20% 的数据:保证是一条链;
  对于 100% 的数据:n, Q ≤ 10^5。

 这道题经过不断的wa终于AC了,因为这题涉及到换根并且要求子树最小值,所以我们可以先O(N) 求出dfs序,同时O(nlogn)可以预处理出父亲数组,然后对于操作,只需看第三个操作即可,设当前根为root,需要查询x子树,我们可以发现,对于最开始的树,root若在x的子树外,则最小值就是x子树内,否则就是整棵树把root所在的x的儿子的那棵子树去掉之后的最小值。而判断root是否在x的子树内,可以由dfs序看出,然后用线段树维护区间最小值以及单点修改即可。(此题建议打非递归,会爆栈,本人比较懒,所以直接开栈了。)

CODE:

{$m 111111111}
var
  f:array[0..100000,0..20] of longint;
  num:array[0..200000] of longint;
  min:array[0..2000000] of longint;
  pre,son:array[0..200000] of longint;
  deep,mark,child,fir,last,data,now,father:array[0..100000] of longint;
  n,q,tot,kid:longint;

  function minn(a,b:longint):longint;
  begin if a<b then exit(a); exit(b); end;

  procedure add(a,b:longint);
  begin
    inc(tot); pre[tot]:=now[a]; now[a]:=tot; son[tot]:=b;
    inc(tot); pre[tot]:=now[b]; now[b]:=tot; son[tot]:=a;
  end;

  procedure dfs(x,fa:longint);
  var p,val:longint;
  begin
      if (fa<>1) and (fa<>0) then
      mark[x]:=mark[fa];
      inc(tot); num[tot]:=x;
      fir[x]:=tot;
      deep[x]:=deep[fa]+1;
      p:=now[x];
      val:=0;
      f[x,0]:=fa;
      while p>-1 do
      begin
       if son[p]<>fa then
       begin
         dfs(son[p],x);
         inc(val);
         mark[son[p]]:=val;
       end;
       p:=pre[p];
      end;
      inc(tot); num[tot]:=x;
      last[x]:=tot;
  end;

  procedure update(x:longint);
  begin
     min[x]:=minn(min[x+x],min[x+x+1]);
  end;

  procedure build(p,l,r:longint);
  var mid:longint;
  begin
      if l>r then exit;
      if l=r then
      begin
       min[p]:=data[num[l]];
       exit;
      end;
      mid:=(l+r) div 2;
      build(p+p,l,mid);
      build(p+p+1,mid+1,r);
      update(p);
  end;

  procedure change(p,l,r,x,v:longint);
  var mid:longint;
  begin
     if (l>x) or (r<x) then exit;
     if (l=r) and (r=x) then
     begin
       min[p]:=v;
       exit;
     end;
     mid:=(l+r) div 2;
     change(p+p,l,mid,x,v);
     change(p+p+1,mid+1,r,x,v);
     update(p);
  end;

  function getfa(x,root:longint):longint;
  var long,tmp:longint;
  begin
      long:=deep[root]-deep[x]-1;
      while long>0 do
      begin
        tmp:=trunc(ln(long)/ln(2));
        root:=f[root,tmp];
        long:=long-(1 shl (tmp));
      end;
      exit(root);
  end;


  function find(p,l,r,ll,rr:longint):longint;
  var mid:longint;
  begin
      if (l=ll) and (r=rr) then exit(min[p]);
      mid:=(l+r) div 2;
      if mid>=rr then
      exit(find(p+p,l,mid,ll,rr))
      else
      if mid<ll then
      exit(find(p+p+1,mid+1,r,ll,rr))
      else
      exit(minn(find(p+p,l,mid,ll,mid),find(p+p+1,mid+1,r,mid+1,rr)));
  end;

  procedure init;
  var i,j,k,a,root,ans,e:longint;
      ch:char;
  begin
    fillchar(now,sizeof(now),$ff);
    readln(n,q); tot:=0; kid:=0;  //ans:=maxlongint;
    data[0]:=maxlongint;
    for i:=1 to n do
    begin
      readln(a,data[i]);
     // ans:=minn(ans,data[i]);
     // if data[i]=1 then
     //  writeln('fuck   ',i);
     // writeln(ans);
      add(i,a);
      if a=1 then
      begin
       inc(kid);
       child[kid]:=i;
       mark[i]:=kid;
      end;
    end;
    //writeln('fuck  ',ans);
    tot:=0;
    dfs(1,0);
    for i:=1 to n do
     for j:=1 to 20 do
      f[i,j]:=f[f[i,j-1],j-1];

    build(1,1,tot);
    root:=1;
    for i:=1 to q do
    begin
     //writeln('fuck  ',root);
     read(ch);
     if ch='Q' then
     begin
      readln(j);
      if root=j then writeln(min[1])
      else
      begin
      if ((fir[root]<fir[j]) and (last[root]>last[j]))
      or (last[root]<fir[j]) or (fir[root]>last[j]) then
      writeln(find(1,1,tot,fir[j],last[j]))
      else
      writeln(minn(find(1,1,tot,1,fir[getfa(j,root)]-1),find(1,1,tot,last[getfa(j,root)]+1,tot)));
      end;
     end;

     if ch='V' then
     begin
     readln(j,k);
     change(1,1,tot,fir[j],k);
     change(1,1,tot,last[j],k);
     end;

     if ch='E' then
     begin
      readln(root);
//      inc(e);
     end;
    //writeln(min[1]);
    end;
//    writeln('fuck   ',e);
  end;
begin
 init;
end.


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值