题目大意: link

有一棵 n 个点的树,根结点为 1 号点,每个点的权值都是 1 或 0

共有 m 次操作,操作分为两种

get 询问一个点 x 的子树里有多少个 1

pow 将一个点 x 的子树中所有节点取反

对于每个 get 给出答案

输入格式:

第一行一个整数 n

第二行共 n−1 个整数,第 i 个数 \(x_i\) 表示 \(x​_i\) 是 i+1 的父亲,

第三行给出每个点的初始权值

第四行一个整数 m

接下来 m 行为操作类型和位置

输入输出样例

输入 #1

4

1 1 1

1 0 0 1

9

get 1

get 2

get 3

get 4

pow 1

get 1

get 2

get 3

get 4

输出 #1

2

0

0

1

2

1

1

0

说明/提示

The tree before the task pow 1.

The tree after the task pow 1.

前言

一道很简单的题,当你刷过其他的树剖题,你就会发现这道题是如此的 So,Easy"

题意

两种操作,一个是求子树中 \(1\) 的个数,另一种是区间取反,即 \(0\)变为\(1\)

\(1\)变为\(0\)。(学过树剖的一眼就切了雾)

前置芝士

dfn序

定义: 节点被遍历的顺序

性质: 1. 子树中dfn序是连续的。

   2. 一条重链上dfn序是连续的(~~没学过树剖的请自行跳过~~)

分析

首先,我们可以遍历整棵树,求出每个点的dfn序,在以dfn序建树。

对于操作一,我们可以线段树维护区间和(由性质1可得子树的dfn序是连续的,

所以区间也是连续的)。

对于操作二,我们发现一个序列连续取两次反,就会变为原来的序列。所以我们

维护一个tag标记,1表示未取反,-1表示取反一次。下放时,孩子节点的tag直接

乘以-1就行了,区间和变为区间长度减去原来的区间和。

几个要注意的点

  1. 标记要初始化为1,而不是0

  2. 下放标记时,孩子节点的标记要乘以-1,而不是变为-1.(因为原来孩子可能

    要取反,现在在取反一次等同于没有取反)。

  3. 下放时区间和变为区间长度减去原来的区间和

好像都是打标记出现的问题(雾)

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 2e5+10;
char opt[10];
int n,v,t,x,tot,num;
int dfn[N],w[N],a[N],size[N],head[N];
inline int read()
{
int s = 0, w = 1; char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
while(ch >= '0' && ch <= '9'){s = s * 10+ch -'0'; ch = getchar();}
return s * w;
}
struct node{int to,net;}e[N<<1];
void add(int x,int y)
{
e[++tot].to = y;
e[tot].net = head[x];
head[x] = tot;
}
void dfs(int x,int fa)//dfs求dfs序
{
size[x] = 1; dfn[x] = ++num; w[dfn[x]] = a[x];
for(int i = head[x]; i; i = e[i].net)
{
int to = e[i].to;
if(to == fa) continue;
dfs(to,x);
size[x] += size[to];
}
}
struct Tree
{
struct node{
int lc,rc;
int tag,sum;
}tr[N<<2];
#define l(o) tr[o].lc
#define r(o) tr[o].rc
#define tag(o) tr[o].tag
#define sum(o) tr[o].sum
void up(int o)
{
sum(o) = sum(o<<1) + sum(o<<1|1);
}
void cover(int o)
{
tag(o) *= -1;
sum(o) = (r(o) - l(o) + 1) - sum(o);
}
void down(int o)//下放标记
{
if(tag(o) == -1)
{
cover(o<<1); cover(o<<1|1);
tag(o) = 1;
}
}
void build(int o,int L,int R)
{
l(o) = L, r(o) = R; tag(o) = 1;//tag初始化一定要为1
if(L == R)
{
sum(o) = w[L]; return;
}
int mid = (L + R)>>1;
build(o<<1,L,mid);
build(o<<1|1,mid+1,R);
up(o);
}
void chenge(int o,int L,int R)//区间取反
{
if(L <= l(o) && R >= r(o))
{
cover(o); return;
}
down(o);
int mid = (l(o) + r(o))>>1;
if(L <= mid) chenge(o<<1,L,R);
if(R > mid) chenge(o<<1|1,L,R);
up(o);
}
int ask(int o,int L,int R)//区间和
{
int ans = 0;
if(L <= l(o) && R >= r(o)) {return sum(o);}
down(o);
int mid = (l(o) + r(o))>>1;
if(L <= mid) ans += ask(o<<1,L,R);
if(R > mid) ans += ask(o<<1|1,L,R);
return ans;
}
}tree;
int main()
{
n = read();
for(int i = 2; i <= n; i++)//习惯了从1开始编号
{
v = read();
add(v,i); add(i,v);
}
for(int i = 1; i <= n; i++) a[i] = read();
dfs(1,1); tree.build(1,1,n);
t = read();
while(t--)
{
scanf("%s",opt+1);
x = read();
if(opt[1] == 'g')//询问子树1的个数
{
printf("%d\n",tree.ask(1,dfn[x],dfn[x] + size[x] - 1));
}
if(opt[1] == 'p')//区间取反
{
tree.chenge(1,dfn[x],dfn[x] + size[x] - 1);
}
}
return 0;
}

ENDING

CF877E Danil and a Part-time Job的更多相关文章

  1. CF877E Danil and a Part-time Job 线段树维护dfs序

    \(\color{#0066ff}{题目描述}\) 有一棵 n 个点的树,根结点为 1 号点,每个点的权值都是 1 或 0 共有 m 次操作,操作分为两种 get 询问一个点 x 的子树里有多少个 1 ...

  2. 2019.2-2019.3 TO-DO LIST

    DP P2723 丑数 Humble Numbers(完成时间:2019.3.1) P2725 邮票 Stamps(完成时间:2019.3.1) P1021 邮票面值设计(完成时间:2019.3.1) ...

  3. Codeforces 877E - Danil and a Part-time Job(dfs序+线段树)

    877E - Danil and a Part-time Job 思路:dfs序+线段树 dfs序:http://blog.csdn.net/qq_24489717/article/details/5 ...

  4. CodeForces 877E Danil and a Part-time Job(dfs序+线段树)

    Danil decided to earn some money, so he had found a part-time job. The interview have went well, so ...

  5. Codeforces 877E Danil and a Part-time Job(dfs序 + 线段树)

    题目链接   Danil and a Part-time Job 题意    给出一系列询问或者修改操作 $pow$ $x$表示把以$x$为根的子树的所有结点的状态取反($0$变$1$,$1$变$0$ ...

  6. Codeforces Round #877 (Div. 2) E. Danil and a Part-time Job

    E. Danil and a Part-time Job 题目链接:http://codeforces.com/contest/877/problem/E time limit per test2 s ...

  7. Codeforces 877E - Danil and a Part-time Job 线段树+dfs序

    给一个有根树,1e5个节点,每个节点有权值0/.1,1e5操作:1.将一个点的子树上所有点权值取反2.查询一个点的子树的权值和   题解: 先深搜整颗树,用dfs序建立每个点对应的区间,等于把树拍扁成 ...

  8. Codeforces Round #442 (Div. 2) E Danil and a Part-time Job (dfs序加上一个线段树区间修改查询)

    题意: 给出一个具有N个点的树,现在给出两种操作: 1.get x,表示询问以x作为根的子树中,1的个数. 2.pow x,表示将以x作为根的子树全部翻转(0变1,1变0). 思路:dfs序加上一个线 ...

  9. Codeforces Round #442 (Div. 2) Danil and a Part-time Job

    http://codeforces.com/contest/877/problem/E 真的菜的不行,自己敲一个模板,到处都是问题.哎 #include <bits/stdc++.h> u ...

随机推荐

  1. 09.redis 哨兵主备切换时数据丢失的解决方案

    一.两种数据丢失的情况 1. 异步复制导致的数据丢失   因为master->slave的复制是异步的,所以可能有部分数据还没复制到slave,master就宕机了,此时这些部分数据就丢失了 2 ...

  2. 使用代码给Unity中的动画片段绑定回调函数

    在制作动作游戏的时候,需要播放许多动画,同时还有个需求,那就是动画播放到一定时间时,给一个回调函数,好做对应的状态变更, 我查了一下,发现如果使用的是unity自带的动画系统,要做到这样的话,需要这样 ...

  3. CA定义以及功能说明

    当您访问以HTTPS开头的网站时,即表示正在使用CA.CA是Internet的重要组成部分.如果不存在CA,那么将无法安全在线购物以及使用网银在线业务等.什么是CA?CA具体是做什么的,又是如何确保您 ...

  4. Linux服务器关联Git,通过执行更新脚本实现代码同步

    1.在Linux服务器安装Git yum install git -y   tips: 卸载Git :  yum remove git   2.在Linux生成ssh key   1)创建用户 git ...

  5. Jwt快速入门(copy即可)

    Jwt 什么是jwt JSON Web Token(缩写 JWT)是目前最流行的跨域认证解决方案,本文介绍它的原理和用法. 互联网服务离不开用户认证.一般流程是下面这样. 1.用户向服务器发送用户名和 ...

  6. C# 压缩源文件(导出源文件word文件)

    说明 1 : 在webUI 公共类,存放 ZipHelper 类 说明  2 :判断文件路径是否存在,不存在则创建文件夹 说明 3  : 引用类方法,判断压缩文件,返回的,是true/false 引用 ...

  7. Codeforces Round #670 (Div. 2) 深夜掉分(A - C题补题)

    1406A. Subset Mex https://codeforces.com/contest/1406/problem/A Example input 4 6 0 2 1 5 0 1 3 0 1 ...

  8. Popular Cows(POJ 2186)

    原题如下: Popular Cows Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 40746   Accepted: 16 ...

  9. [POJ]Find The Multiple(DFS)

    题目链接 http://poj.org/problem?id=1426 题意 输入一个数n,输出任意一个 只含0.1且能被n整除的数m.保证n<=200,m最多100位. 题解 DFS/BFS都 ...

  10. 《Offer一箩筐》2W字总结面试套路14问——不给例子的教程都是耍流氓!!

    「MoreThanJava」 宣扬的是 「学习,不止 CODE」. 如果觉得 「不错」 的朋友,欢迎 「关注 + 留言 + 分享」,文末有完整的获取链接,您的支持是我前进的最大的动力! Hi~ 这里是 ...