您的位置:首页 > 其它

ZOJ 3686 A A Simple Tree Problem

2013-04-02 22:19 274 查看
开始想了一个错误的算法,对每个节点保存它的子树中的节点数目和其中为一的数目,操作某节点时向上更新其祖先的数据(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
;
int first
;
int next
;
int num
;
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: