zoj 3686 树转化为数组表示,线段树区间更新

A Simple Tree Problem

Time Limit: 3 Seconds       Memory Limit: 65536 KB

Given a rooted tree, each node has a boolean (0 or 1) labeled on it. Initially, all the labels are 0.

We define this kind of operation: given a subtree, negate all its labels.

And we want to query the numbers of 1's of a subtree.

Input

Multiple test cases.

First line, two integer N and M, denoting the numbers of nodes and numbers of operations and queries.(1<=N<=100000, 1<=M<=10000)

Then a line with N-1 integers, denoting the parent of node 2..N. Root is node 1.

Then M lines, each line are in the format "o node" or "q node", denoting we want to operate or query on the subtree with root of a certain node.

Output

For each query, output an integer in a line.

Output a blank line after each test case.

Sample Input

3 2
1 1
o 2
q 1

Sample Output

1


Author:  CUI, Tianyi
Contest:  ZOJ Monthly, March 2013


先用dfs对树进行先序遍历并记录下每个节点的位置pos[rt]和它的儿子数tot[rt],这样就将同一棵子树按先序顺序保存在连续的数组区间,然后就转化为线段树求解区间和问题,遍历+区间更新O(n+mlogn)复杂度


#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>

using namespace std;


#define maxn 100005

vector<int> g[maxn];    //保存每个节点的儿子
int tot[maxn];  //每个节点的儿子数
int pos[maxn];  //每个节点转为数组后对应的位置
int n, m;

int count(int rt, int &cur) //先序遍历确定位置,并统计儿子数
{
    pos[rt] = cur++;
    int sum = 1;
    for(int i = 0; i < g[rt].size(); i++)
        sum += count(g[rt][i], cur);

    tot[rt] = sum-1;
    return sum;
}

int begin[4*maxn], end[4*maxn], sum[4*maxn];
bool lazy[4*maxn];

void build(int rt, int l, int r)
{
    begin[rt] = l; end[rt] = r;
    sum[rt] = 0; lazy[rt] = 0;
    if(l == r)
        return;

    int m = (l+r)>>1;
    build(2*rt, l, m);
    build(2*rt+1,m+1,r);
}

int query(int rt, int ll, int rr)
{
    int lc = 2*rt, rc =2*rt+1;
    if(ll == begin[rt] && rr == end[rt])
        return sum[rt];
    else{
        if(lazy[rt]){
            sum[lc] = end[lc]-begin[lc]+1-sum[lc];
            sum[rc] = end[rc]-begin[rc]+1-sum[rc];
            lazy[lc] = !lazy[lc];
            lazy[rc] = !lazy[rc];
            lazy[rt] = 0;
        }
    }

    int m = (begin[rt]+end[rt])>>1;
    if(rr <= m)
        return query(lc, ll, rr);
    else if(ll > m)
        return query(rc, ll, rr);
    else
        return query(lc,ll,m)+query(rc,m+1,rr);
}

void modify(int rt, int ll, int rr)
{
    int lc = 2*rt, rc = 2*rt+1;
    if(ll == begin[rt] && rr == end[rt]){
        sum[rt] = end[rt]-begin[rt]+1-sum[rt];
        lazy[rt] = !lazy[rt];
        return;
    }
    else{
        if(lazy[rt]){
            sum[lc] = end[lc]-begin[lc]+1-sum[lc];
            sum[rc] = end[rc]-begin[rc]+1-sum[rc];
            lazy[lc] = !lazy[lc];
            lazy[rc] = !lazy[rc];
            lazy[rt] = 0;
        }
    }

    int m = (end[rt]+begin[rt])>>1;
    if(rr <= m)
        modify(lc, ll, rr);
    else if(ll > m)
        modify(rc, ll, rr);
    else{
        modify(lc, ll, m);
        modify(rc, m+1, rr);
    }

    sum[rt] = sum[lc]+sum[rc];
}

int main()
{
    while(~scanf("%d %d", &n, &m)){
        for(int i = 0; i <n+2; i++) g[i].clear();
        int t;
        for(int i = 0; i < n-1; i++){
            scanf("%d", &t);
            g[t].push_back(i+2);
        }

        int cur = 1;
        count(1,cur);
        build(1,1,n);

        char op[2];
        int num;
        for(int i = 0 ; i < m; i++){
            scanf("%s %d", op, &num);

            if(op[0] == 'o')
                modify(1, pos[num], pos[num]+tot[num]);
            else
                printf("%d\n", query(1, pos[num], pos[num]+tot[num]));
        }
       printf("\n");
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值