题目大意: 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. Android开发之http网络请求返回码问题集合。

    HTTP状态码(HTTP Status Code) 一些常见的状态码为: 200 - 服务器成功返回网页  404 - 请求的网页不存在  503 - 服务不可用  一.1xx(临时响应) 表示临时响 ...

  2. iOS打电话功能的简单实现

    小功能简介 iOS中的很多小功能都是非常简单的,几行代码就搞定了,比如打电话.打开网址.发邮件.发短信等 打电话-方法3 创建一个UIWebView来加载URL,拨完后能自动回到原应用 if (_we ...

  3. ACwing 你能回答这些问题吗(线段树求最大连续字段和)

    给定长度为N的数列A,以及M条指令,每条指令可能是以下两种之一: 1.“1 x y”,查询区间 [x,y] 中的最大连续子段和,即 maxx≤l≤r≤ymaxx≤l≤r≤y{∑ri=lA[i]∑i=l ...

  4. Left Mouse Button (bfs)

    Mine sweeper is a very popular small game in Windows operating system. The object of the game is to ...

  5. Stack (30)(模拟栈,输出中间数用set)

    Stack is one of the most fundamental data structures, which is based on the principle of Last In Fir ...

  6. Redis Sentinel结构 及相关文档

    Redis Sentinel是一个用来监控redis集群中节点的状态,不用来存储数据.当集群中的某个节点有故障时,可以自动的进行故障转移的操作.通常为了保证sentinel的高可用,sentinel也 ...

  7. unzip命令笔记

    unzip命令 文件压缩与解压 unzip命令用于解压缩由zip命令压缩的".zip"压缩包. 语法 unzip(选项)(参数) 选项 -c:将解压缩的结果显示到屏幕上,并对字符做 ...

  8. 关于while (~scanf("%d %d", &m, &n))的用法

    其功能是循环从输入流读入m和n,直到遇到EOF,有如下关系: while (~scanf("%d %d", &m, &n)) ↔ while (scanf(&quo ...

  9. python里面的project、package、module分别是什么

    2020/5/25 1.project(项目) project 即项目,是一个很大的文件夹,里面有好多的 .py 文件. 在Spyder 中点击菜单栏 projects ----->  new ...

  10. matlab数字图像处理-冈萨雷斯-数据类和图像类之间的转换

    亮度图像 二值图像 属于注释 数据类间的转换 图像类和类型间的转化 把一个double类的任意数组转换成[0,1]的归一化double类数组----->mat2gray 图像类和类型间的转化例题 ...