ZOJ 3686 A Simple Tree Problem(线段树)

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

题意:给你一颗树,并且有两种操作,一种是将以节点i为root的子树每个节点上的值取反,还有一种操作时

查询以节点i为root的子树所有节点上的值得和。节点上的值不是1就是0,初始皆为0。

题解:把子树化为区间的就是裸的线段树了。我们可以先序遍历树,把以x为根节点的子树的用L[x],R[x]区间表示

然后就是线段树区间更新了。

#include<cstring>
#include<cstdio>
#include<iostream>
#include<cmath>
#include<vector>
#include<algorithm>
#define lc idx<<1
#define rc idx<<1|1
#define lson l,mid,lc
#define rson mid+1,r,rc
#define N 100010

using namespace std;

int n,m;
vector<int>G[N];
int L[N],R[N],id;

struct Tree {
    int st;
    int one;
} tree[N<<2];

void init() {
    for(int i=0; i<=n; i++)
        G[i].clear();
    id=1;
}

void dfs(int fa) {
    L[fa]=id++;
    for(int i=0; i<G[fa].size(); i++) {
        dfs(G[fa][i]);
    }
    R[fa]=id-1;
}

void push_up(int idx) {
    tree[idx].one=tree[lc].one+tree[rc].one;
}

void push_down(int idx,int m) {
    if(tree[idx].st) {
        tree[lc].st^=1,tree[rc].st^=1;
        tree[idx].st=0;
        tree[lc].one=m-m/2-tree[lc].one;
        tree[rc].one=m/2-tree[rc].one;
    }
}

void build(int l,int r,int idx) {
    tree[idx].one=0;
    tree[idx].st=0;
    if(l==r)return;
    int mid=(l+r)>>1;
    build(lson);
    build(rson);
}

void update(int l,int r,int idx,int x,int y) {
    if(x<=l&&r<=y) {
        tree[idx].st^=1;
        tree[idx].one=r-l+1-tree[idx].one;
        return;
    }
    push_down(idx,r-l+1);
    int mid=(l+r)>>1;
    if(x<=mid)update(lson,x,y);
    if(y>mid) update(rson,x,y);
    push_up(idx);
}

int query(int l,int r,int idx,int x,int y) {
    if(x<=l&&r<=y)
        return tree[idx].one;
    push_down(idx,r-l+1);
    int mid=(l+r)>>1;
    int ans=0;
    if(x<=mid) ans+=query(lson,x,y);
    if(y>mid)  ans+=query(rson,x,y);
    return ans;
}

int main() {
    //freopen("test.in","r",stdin);
    while(~scanf("%d%d",&n,&m)) {
        init();
        int fa;
        for(int i=2; i<=n; i++) {
            scanf("%d",&fa);
            G[fa].push_back(i);
        }
        dfs(1);
        build(1,n,1);
        char c[3];
        int rt;
        while(m--) {
            scanf("%s%d",c,&rt);
            if(c[0]=='o') {
                update(1,n,1,L[rt],R[rt]);
                continue;
            }
            printf("%d\n",query(1,n,1,L[rt],R[rt]));
        }
        printf("\n");
    }
    return 0;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-12 16:04:29

ZOJ 3686 A Simple Tree Problem(线段树)的相关文章

zoj 3686 A Simple Tree Problem (线段树)

Solution: 根据树的遍历道的时间给树的节点编号,记录下进入节点和退出节点的时间.这个时间区间覆盖了这个节点的所有子树,可以当做连续的区间利用线段树进行操作. /* 线段树 */ #pragma comment(linker, "/STACK:102400000,102400000") #include <iostream> #include <cstdio> #include <cstring> #include <cmath>

hdu 4973 A simple simulation problem.(线段树)

http://acm.hdu.edu.cn/showproblem.php?pid=4973 有两种操作 D l r 将[l,r]区间翻倍 Q l r询问[l,r]中相同数字出现的最多次数 比赛的时候脑子太乱了,没有想到怎么做.发现每次翻倍序列的长度都在变化,区间对应的数也在变,没有思路. 但是静下心来想一想,思路还是挺清晰的. 无论怎么翻倍,序列中的数都是连续的,范围是1~n.可以拿一个数组来记录每个数出现的次数,当更新或询问区间[l,r]时,可以利用其前缀和找到区间[l,r]对应的数字分别是

HDU4973A simple simulation problem.(线段树,区间更新)

A simple simulation problem. Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 330    Accepted Submission(s): 132 Problem Description There are n types of cells in the lab, numbered from 1 to n.

bzoj 3489 A simple rmq problem - 线段树

Description 因为是OJ上的题,就简单点好了.给出一个长度为n的序列,给出M个询问:在[l,r]之间找到一个在这个区间里只出现过一次的数,并且要求找的这个数尽可能大.如果找不到这样的数,则直接输出0.我会采取一些措施强制在线. Input 第一行为两个整数N,M.M是询问数,N是序列的长度(N<=100000,M<=200000) 第二行为N个整数,描述这个序列{ai},其中所有1<=ai<=N 再下面M行,每行两个整数x,y, 询问区间[l,r]由下列规则产生(OIER

HDU-4937-A simple simulation problem.(线段树)

Problem Description There are n types of cells in the lab, numbered from 1 to n. These cells are put in a queue, the i-th cell belongs to type i. Each time I can use mitogen to double the cells in the interval [l, r]. For instance, the original queue

HDU4973:A simple simulation problem.(线段树)

Problem Description There are n types of cells in the lab, numbered from 1 to n. These cells are put in a queue, the i-th cell belongs to type i. Each time I can use mitogen to double the cells in the interval [l, r]. For instance, the original queue

2014 Super Training #9 F A Simple Tree Problem --DFS+线段树

原题: ZOJ 3686 http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3686 这题本来是一个比较水的线段树,结果一个mark坑了我好几个小时..哎.太弱. 先DFS这棵树,树形结构转换为线性结构,每个节点有一个第一次遍历的时间和最后一次遍历的时间,之间的时间戳都为子树的时间戳,用线段树更新这段区间即可实现更新子树的效果,用到懒操作节省时间. 坑我的地方: update时,不能写成:tree[rt].mark = 1,

BNU 28887——A Simple Tree Problem——————【将多子树转化成线段树+区间更新】

A Simple Tree Problem Time Limit: 3000ms Memory Limit: 65536KB This problem will be judged on ZJU. Original ID: 368664-bit integer IO format: %lld      Java class name: Main Prev Submit Status Statistics Discuss Next Type: None None Graph Theory 2-SA

ZOJ 2671 -Cryptography ( 矩阵乘法 + 线段树 )

ZOJ 2671 - Cryptography ( 矩阵乘法 + 线段树 ) 题意: 给定模数r, 个数n, 询问数m 然后是n个矩阵,每次询问,输出矩阵联乘之后的结果. 分析: 矩阵乘法 + 线段树优化 这里线段树只有询问没有更新操作. PS:顺便仰慕一下watashi.... 代码: #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> using names