北大OJ 题目地址

【题意】
在卡卡的房子外面有一棵苹果树,树上有N 个叉(编号为1~N ,根为1),它们通过分支连接。苹果在叉上生长,两个苹果不会在同一个叉上生长。一个新的苹果可能会在一个空叉上长出来,卡卡还可能会从树上摘一个苹果作为他的甜点。
卡卡想了解一棵子树上有多少苹果。

【输入输出】
输入:
第1行包含一个整数N (N ≤100,000),表示树中叉的数量。以下N -1行,每行都包含两个整数u 和v ,表示叉u 和叉v 通过分支连接。下一行包含整数M (M ≤100,000)。以下M 行,每行都包含一个消息,C x 表示改变x 叉上的苹果状态。若叉上有苹果,则卡卡会选择摘掉它,否则一个新的苹果在这个空叉上长大;Q x 表示查询x 叉上方子树中的苹果数量,包括x 叉上的苹果(若存在)。
注意:开始时树上长满了苹果。
输出:
对每个查询,都单行输出答案。
【样例】

【思路分析】
本题包含两种操作,一种是点更新,一种是查询以当前节点为根的子树的苹果数量。
点更新很简单,那么如何得到以当前节点为根的子树的苹果数量呢?
若将一棵树深度遍历,则记录遍历时当前节点进来和出去时的序号,两个序号之间的节点就是当前节点的子树节点。可以利用DFS序将子树转换为序列,然后求解区间和。
【算法设计】
① 根据输入的分支构建树。
② 采用深度遍历求树的DFS序列,记录进出i 节点的序号L[i ]和R[i ]。
③ Q x :查询以x 节点为根的子树中的苹果数量,只需计算进出x 节点的区间和[L[x ], R[x ]],即sum(R[x ])-sum(L[x ]-1)。
④ C x :若判断x 节点的值为1,则在树状数组中点更新-1,否则+1。然后a [x ]^=1,进行异或运算,1变为0,0变为1。
【举个栗子】
输入数据如下。

① 构建一棵树,深度优先遍历的dfs序列如下图所示。

② 节点i 进来和出去时的序号如下:

③ 查询或更新操作。
Q 1:查询以1号节点为根的子树中的苹果数量。1号节点的进出序号为L[1]=1,R[1]=5,查询[1, 5]的区间和,sum(R[1])-sum(L[1]-1)=5-0=5,所以1号节点的子树中的苹果数量为5。
Q 3:查询以3号节点为根的子树中的苹果数量。3号节点的进出序号为L[3]=3,R[3]=5,查询[3, 5]的区间和,sum(R[3])-sum(L[3]-1)=5-2=3。所以3号节点的子树中的苹果数量为3。
C 2:改变2号节点的苹果状态。2号节点有苹果(值为1),在树状数组中点更新-1,然后a [2]^=1,进行异或运算,1变为0,0变为1,此时a [2]=0。
Q 1:查询以1号节点为根的子树中的苹果数量,1号节点的进出序号为L[1]=1、R[1]=5,查询[1, 5]的区间和,sum(R[1])-sum(L[1]-1)=4-0=5。所以1号节点的子树中的苹果数量为4。
【算法实现】
#include
#include
#include
#include
#includeusing namespace std;const int maxn=1e5+10;int N,q;
int c[maxn];
int a[maxn];
int L[maxn],R[maxn];
int head[maxn];
int cnt;
int dfn;struct edge{int u,v;int next;
}E[2*maxn];void adde(int u,int v){E[++cnt].u=u;E[cnt].v=v;E[cnt].next=head[u];head[u]=cnt;
}int lowbit(int x){return x&(-x);
}void add(int i,int v){for(;i<=N;i+=lowbit(i))c[i]+=v;
}int sum(int i){int ans=0;for(;i>0;i-=lowbit(i)){ans+=c[i];}return ans;
}void init(){memset(c,0,sizeof(c));memset(L,0,sizeof(L));memset(R,0,sizeof(R));memset(head,0,sizeof(head));cnt=0;dfn=1;for(int i=1;i<=N;i++){a[i]=1;add(i,1);}
}void dfs(int u,int fa){L[u]=dfn++;for(int i=head[u];i;i=E[i].next){int v=E[i].v;if(v==fa) continue;dfs(v,u);}R[u]=dfn-1;
}int main(){scanf("%d",&N);int u,v;init();for(int i=1;iscanf("%d%d",&u,&v);adde(u,v);}dfs(1,1);scanf("%d",&q);char op[10];for(int i=1;i<=q;i++){scanf("%s %d",op,&v);if(op[0]=='C'){if(a[L[v]])add(L[v],-1);elseadd(L[v],1);a[L[v]]^=1;}else{int s1=sum(R[v]);int s2=sum(L[v]-1);printf("%d\n",s1-s2);}}return 0;
}

下一篇:七尺男儿是啥意思