ZOJ 3686 A A Simple Tree Problem

来源:互联网 发布:华悦网络加速器怎么样 编辑:程序博客网 时间:2024/06/05 18:24

开始想了一个错误的算法,对每个节点保存它的子树中的节点数目和其中为一的数目,操作某节点时向上更新其祖先的数据(log N),查询时直接输出。当时忽略了操作同时也需要更新后代的数据,这样的最坏复杂度是N,肯定是不行的。但直接交上去不是WA而是超时,由此猜想其中有类似链表的数据。

这个问题可以用线段树很好地解决,在对原树进行先序遍历的同时标上序号后,可以将任意子树转化为区间。序号介于某节点到它的任意后代间的点必为该节点的后代,在这种排序下即可建立线段树,将对子树的操作转化为对区间的操作。


#include <stdio.h>#include <memory.h>#define N 100001#define M 262144#define Negate(x) label[x]=!label[x]#define Comple(a, b) sum[a]=b-sum[a]int right[N];int first[N];int next[N];int num[N];int sum[M];bool label[M];int a, b, ans;int n, m, cnt;void build(int index){int i = first[index];num[index] = ++ cnt;right[index] = cnt;while(i > 0){build(i);if(right[i] > right[index])right[index] = right[i];i = next[i];}}void update(int cur, int l, int r){if(a<=l && b>=r){Negate(cur);Comple(cur, r-l+1);}else{int mid = (l+r) >> 1;int x = cur<<1, y = x+1;if(label[cur] == true){Negate(cur);Negate(x);Negate(y);Comple(x, mid-l+1);Comple(y, r-mid);}if(a <= mid)update(x, l, mid);if(b > mid)update(y, mid+1, r);sum[cur] = sum[x] + sum[y];}}void query(int cur, int l, int r){if (a<=l && b>=r)ans += sum[cur];else{int mid = (l+r) >> 1;int x = cur<<1, y = x+1;if(label[cur] == true){Negate(cur);Negate(x);Negate(y);Comple(x, mid-l+1);Comple(y, r-mid);}if(a <= mid)query(x, l, mid);if(b > mid)query(y, mid+1, r);sum[cur] = sum[x] + sum[y];}}int main(){int node;while(scanf("%d%d", &n, &m) != EOF){memset(first, 0, sizeof(first));for(int i=2; i<=n; ++i){scanf("%d", &node);next[i] = first[node];first[node] = i;}cnt = 0;build(1);memset(label, false, sizeof(label));memset(sum, 0, sizeof(sum));char c[5];for(int j=0; j<m; ++j){scanf("%s", c);scanf("%d", &node);a = num[node];b = right[node];if(c[0] == 'q'){ans = 0;query(1, 1, n);printf("%d\n", ans);}else if(c[0] == 'o')update(1, 1, n);}printf("\n");}return 0;}


原创粉丝点击