题目大意: 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. Hexo-butterfly-magicv3.0.1(持续更新中....)

    介绍 Hexo-butterfly魔改v3.0.1 软件架构 本项目是基于Hexo静态博客的个性主题---蝴蝶主题魔改版 安装教程 克隆 安装依赖 hexo命令生成public文件夹 启动hexo-s ...

  2. unity 模板测试 详解

    https://blog.csdn.net/u011047171/article/details/46928463#t4

  3. Unity Plugins的使用方法

    一.为插件设置平台的方法 unity5之前,是通过把插件搞到对应目录进行区分平台的(比如在build target是ios平台时只把IOS目录的插件build进去),unity5之后提供了设置平台/c ...

  4. es6使用箭头函数需要注意的问题

    this问题 箭头函数没有它自己的this值,箭头函数内的this值继承自外围作用域,谁定义的函数,this指向谁 箭头函数要实现类似纯函数的效果,必须剔除外部状态.所以箭头函数不具备普通函数里常见的 ...

  5. JavaScript(es6)数组常用的方法

    常用方法 1.forEach() var data = [1,2,3,4,5]; var sum = 0;//求和 data.forEach((item)=>{sum+=item}) //给原数 ...

  6. KMP - NOI2014 动物园

    单题分析:NOI2014 动物园. 题目分析:很明显题目已明确指出这是有关KMP的题,思考KMP.本题与普通KMP不同之处在于它求的是不相交最长相同前缀后缀. 如何处理不相交: 1.暴力     2. ...

  7. opencv-python函数

    opencv-python读取.展示和存储图像 1.imshow函数 imshow函数作用是在窗口中显示图像,窗口自动适合于图像大小,我们也可以通过imutils模块调整显示图像的窗口的大小.函数官方 ...

  8. basicInterpreter1.02 增加对for循环的支持

    源码下载:https://files.cnblogs.com/files/heyang78/basicInterpreter102-20200531-2.rar 输入: for x= to print ...

  9. UNIX编程艺术

    本文主要是 <UNIX编程艺术>的摘录,摘录的主要是我觉得对从事软件开发有用的一些原则. 对于程序员和开发人员来说,如果完成某项任务所需要付出的努力对他们是个挑战却又恰好还在力所能及的范围 ...

  10. Win10更新后蓝牙出现故障的解决方法

    昨天Win10自动更新后,我发现我的键盘突然就不管用了,检查了一下发现原来蓝牙没有打开,同时任务栏中的蓝牙图标也不见了. 不久之前,这样的情况已经出现过了一次,那次好像更新系统后就好了,但这次是系统更 ...