[树状数组] Apple

【问题描述】

有一棵n个结点的树,开始时每个结点上都有一个苹果。每次有两种操作:

C x:如果结点x上有苹果,那么就摘下它,否则结点x上会再生出一个苹果。

Q x:询问以结点x为根的子树中苹果的个数。

你要对于每个Q操作,输出相应的答案。

【输入文件:apple.in】

第一行:一个整数n,表示树的结点个数,以1为根结点。接下来n-1行:每行两个整数ui,vi,表示树的一条边。再接下来是一个整数m,表示操作的数目,接下来m行为m个操作,格式同题目所述。

【输出文件:apple.out】

对于每个Q操作,逐行输出相应的答案。

【样例输入】

6

1 2

1 3

1 4

2 5

2 6

3

Q 1

C 2

Q 1

【样例输出】

6

5

【数据规模】

对于30%的数据,n,m≤1000。

对于100%的数据,n,m≤100000。

【算法分析】

1. 用tree数组邻接链表存储无向图(设想可否用有向图实现?)

2. 对这棵树进行先根序遍历并给各个结点重新赋值下标:v[i]=j表示结点i的新下标为j。{实际也是离散化的过程} 并算出当前结点i及其子树的最大新下标:maxv[i]=j 表示结点编号为i及其子树的最大新下标为j,那么当前结点所覆盖的范围就是[最小下标,最大下标]了。这一条有点难理解,表示我虽然写出程序了仍然略理解不能。

3. 树状数组 c[i]用于存放树状划分后的前缀和

4. 对于C操作调用add;对于Q操作输出sum(maxv[x])-sum(v[x]-1)

【程序描述】

tree数组 tree[i].x 表示该结点编号 tree[i]^.next 指向下一结点

f数组 f[i]=true表示该节点已被访问 false表示未访问过

a数组 a[i]=1表示i结点上的苹果未被摘下 0表示已摘下

v数组 v[i]=j表示i结点的新下标为j

c数组 存放树状划分的前缀和

maxv数组 maxv[i]=j表示结点i及其子树的最大新下标是j

lowbit函数,add函数,getsum函数 参考birtree

dfs函数 程序的亮点和精华也是最难理解的部分。先根序遍历图,重新编号,done表示已至叶结点,因为递归调用dfs后一定会到叶子节点;同时会有done=false是因为tree数组按照无向图的结构建立,同一条边实际在tree数组中记录了两次,因此不用重复访问。这个是个人理解,可能有不对的地方。
【源代码】

Apple
program apple;
type treenode=^rec;
     rec=record
        x:longint;
        next:treenode;
     end;
var tree:array[0..100000] of treenode;
    f:array[0..100000] of boolean;
    a,c,v,maxv:array[0..100000] of longint;
    n,m:longint;
function lowbit(i:longint):longint;
begin
  lowbit:=i - (i and (i-1));
end;

procedure add(i,x:longint);
begin
  while i<=n do
    begin
      c[i]:=c[i]+x;
      i:=i+lowbit(i);
    end;
end;

function sum(i:longint):longint;
var total:longint;
begin
  total:=0;
  while i>0 do
    begin
      total:=total+c[i];
      i:=i-lowbit(i);
    end;
  exit(total);
end;

function dfs(x:longint):longint;
var now:longint;done:boolean;
    p:treenode;
begin
  done:=false;
  inc(m);v[x]:=m;
  p:=tree[x]^.next;
  f[x]:=false;
  while p<>nil do
    if f[p^.x] then
      begin
        now:=dfs(p^.x);
        p:=p^.next;
        if maxv[x]<now then maxv[x]:=now;
        done:=true;
      end
    else p:=p^.next;
  if not done then
    begin
      maxv[x]:=m;
      exit(m);
    end
  else exit(maxv[x]);
end;
procedure work;
var m,x,i:longint;
    func:char;
begin
  readln(m);
  for i:=1 to m do
    begin
      read(func);
      readln(x);
      if func='Q' then
        writeln(sum(maxv[x])-sum(v[x]-1));
      if func='C' then
        if a[v[x]]=1 then
          begin
            a[v[x]]:=0;
            add(v[x],-1);
          end
        else
          begin
            a[v[x]]:=1;
            add(v[x],1);
          end;
    end;
end;

procedure init;
var i,x,y:longint;
    p:treenode;
begin
  fillchar(f,sizeof(f),true);
  readln(n);
  for i:=1 to n do
    begin
      new(p);
      tree[i]:=p;
      a[i]:=1;
      add(i,1);
    end;
  for i:=1 to n-1 do
    begin
      readln(x,y);
      new(p);
      p^.x:=y;
      p^.next:=tree[x]^.next;
      tree[x]^.next:=p;
      new(p);
      p^.x:=x;
      p^.next:=tree[y]^.next;
      tree[y]^.next:=p;
    end;
  maxv[1]:=dfs(1);
end;

begin {main}
  assign(input,'apple.in');reset(input);
  assign(output,'apple.out');rewrite(output);
  init;
  work;
  close(input);close(output);
end.

 

 

转载于:https://www.cnblogs.com/Sky-Grey/archive/2013/03/03/2941551.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值