【问题描述】
有一棵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数组中记录了两次,因此不用重复访问。这个是个人理解,可能有不对的地方。
【源代码】
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.