当初听郭炜老师讲时不是很懂,几个月内每次复习树状数组必看的题
树的dfs序映射在树状数组上进行单点修改,区间查询。
/* 树状数组: lowbit[i] = i&-i C[i] = a[i-lowbit[i]+1]+...+a[i] 求和: 设sum[k] = a[1]+a[2]+...+a[k] 则a[i]+a[i+1]+...+a[j] = sum[j]-sum[i-1] 在树状数组上:sum[k] = C[n1]+C[n2]+...+C[k] n1 = n2-lowbit[n2]... >0 更新: 如果a[i]更新了,则以下几项也要更新 C[n1], C[n2], ... C[nm] n1 = i, n2 = n1+lowbit[n1].. nm<=N 建树:O(N) C[k] = sum[k]-sum[k-lowbit[k]] poj3321 树状数组:单点更新 书上长满了苹果,每一个节点有长苹果和不长两种状态 操作1:改变树枝上有无苹果的状态 操作2:询问某一树枝节点以下的所有的苹果有多少 做一次dfs,几下每个节点的开始时间Start[i]和结束时间End[i] 对于i节点的所有子孙的开始时间和结束时间都应该位于Start[i]和End[i]之间 C[i]是树状数组节点对应的苹果数 然后用树状数组C统计Start[i]到End[i]之间的附加苹果总数 树状数组统计区间可以用Sum(End[i])-Sum(Start[i]-1)来计算 */ #include<iostream> #include<cstring> #include<cstdio> #include<vector> #define MAXN 220000 using namespace std; int C[MAXN]; vector<vector<int> > G(MAXN/2);//树 int Lowbit[MAXN]; int Start[MAXN]; int End[MAXN]; bool HasApple[MAXN/2]; int nCount; //dfs搜索出所有状态 void dfs(int v){ Start[v] = ++nCount; for (int i = 0; i != G[v].size(); i++) dfs(G[v][i]); End[v] = ++nCount; } //求1-p的和 int QuerySum(int p){ int nSum = 0; while(p > 0){ nSum += C[p]; p -= Lowbit[p]; } return nSum; } //修改节点p的值 void Modify(int p, int val){ while(p <= nCount){ C[p] += val; p += Lowbit[p]; } } int main(){ int n; scanf("%d", &n); int x, y; int i, j, k; for (i = 0; i < n-1; i++){ int a, b; scanf("%d%d", &a, &b); G[a].push_back(b); } nCount = 0; dfs(1);//从根节点开始dfs //树状数组要处理的原石数组下标范围 for (i = 1; i <= nCount; i++) Lowbit[i] = i&(-i); for(i = 1; i <= n; i++) HasApple[i] = 1; int m; //建树求C数组,即树状数组的节点值 for(i = 1; i <= nCount; i++) C[i] = i-(i-Lowbit[i]); //C[i] = Sum[i] - Sum[i-Lowbit[i]]; scanf("%d", &m); for (int i = 0; i < m; i++){ char cmd[10]; int a; scanf("%s%d", cmd, &a); //改变某个节点上的苹果的状态 if (cmd[0] == 'C') { if(HasApple[a] == 1){ Modify(Start[a], -1); Modify(End[a], -1); HasApple[a] = 0; } else { Modify(Start[a], 1); Modify(End[a], 1); HasApple[a] = 1; } } //查询某个节点上的苹果 else { int t1 = QuerySum(Start[a]-1); int t2 = QuerySum(End[a]); cout << (t2-t1)/2 << endl; } } }