posted on 2019-10-07 20:21:57
Apple Tree 2
题目描述
有一颗 \(n\) 个节点的苹果树,最初每个节点上都有 \(1\) 个苹果,并且每个节点在操作的过程中只会处在有 \(1\) 个苹果和没有苹果两种状态之一。
有 \(m\) 个操作,操作分为三种:
发动丰收魔法:令节点 \(u\) 为根的子树中所有没有苹果的节点全部长出 个苹果。
摘下苹果吃:令节点 \(u\) 为根的子树中所有有苹果的节点的苹果全部消失。
统计苹果个数:统计节点 \(u\) 为根的子树中苹果的个数。
输入格式
从标准输入读入数据。
第一行输入三个正整数 \(n(n \leq 10^5), m(m \leq 10^5)\) 和 \(root(root \leq n)\),其中 \(root\) 代表根节点编号。
接下来 \(n - 1\) 行,每行输入两个整数 \(x\) 和 \(y(1 \leq x, y \leq n)\),代表 \(x\) 和 \(y\) 之间连有边。
接下来 \(m\) 行,每行输入两个整数 \(opt\)(取值 \(0\) 或 \(1\) 或 \(2\))和 \(u(1 \leq u \leq n)\),其中\(opt = 0\) 表示这是一次丰收魔法、\(opt = 1\) 表示这是一次摘苹果、\(opt = 2\) 表示这是一次统计。
输出格式
输出到标准输出。
对于每个统计操作,输出一行一个整数代表所求答案。
样例输入
5 9 1 2 1 3 2 4 1 5 2 1 1 0 2 2 3 2 2 0 4 2 1 0 1 1 2 2 1
样例输出
1 3 4 2
很好的一个线段树与\(DFS\)序结合的题目
因为一棵树的子树的\(DFS\)序是连续的,因此直接按\(DFS\)序建线段树,子树修改与查询都变为了区间修改与查询
代码如下
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <cstdlib> #include <algorithm> using namespace std; const int N = 1e5 + 3; int n, m, rt; struct edge { int to, next; } e[N << 1]; int head[N], cnt, a[N], vis[N], size[N], pos[N], sum[N << 2], tag[N << 2]; void add(int u, int v) { e[++cnt].to = v; e[cnt].next = head[u]; head[u] = cnt; } void dfs(int k) { size[k] = 1; vis[k] = 1; for(int i = head[k]; i; i = e[i].next) { int v = e[i].to; if(!vis[v]) { a[++cnt] = v; pos[v] = cnt; dfs(v); size[k] += size[v]; } } } //int dfn[N][2], tm; //void dfs(int u) { // vis[u] = 1; // dfn[u][0] = ++tm; // for(int i = head[u]; i; i = e[i].next) { // int v = e[i].to; // if(!vis[v]) // dfs(v); // } // dfn[u][1] = tm; //} void pushup(int u) { sum[u] = sum[u << 1] + sum[u << 1 | 1]; } void build(int u, int ul, int ur) { tag[u] = -1; if(ul == ur) { sum[u] = 1; return; } int mid = ul + ur >> 1; build(u << 1, ul, mid); build(u << 1 | 1, mid + 1, ur); pushup(u); } void update(int u, int ul, int ur, int x) { if(x == 0) sum[u] = ur - ul + 1; else sum[u] = 0; tag[u] = x; } void pushdown(int u, int ul, int ur) { if(tag[u] != -1) { int mid = ul + ur >> 1; update(u << 1, ul, mid, tag[u]); update(u << 1 | 1, mid + 1, ur, tag[u]); tag[u] = -1; } } void modify(int u, int ul, int ur, int ml, int mr, int mx) { if(ul >= ml && ur <= mr) { update(u, ul, ur, mx); return; } pushdown(u, ul, ur); int mid = ul + ur >> 1; if(mid >= ml) modify(u << 1, ul, mid, ml, mr, mx); if(mid + 1 <= mr) modify(u << 1 | 1, mid + 1, ur, ml, mr, mx); pushup(u); } int query(int u, int ul, int ur, int ml, int mr) { if(ml <= ul && mr >= ur) { return sum[u]; } pushdown(u, ul, ur); int mid = ul + ur >> 1; int s = 0; if(mid >= ml) { s += query(u << 1, ul, mid, ml, mr); } if(mid + 1 <= mr) { s += query(u << 1 | 1, mid + 1, ur, ml, mr); } return s; } int main() { cin >> n >> m >> rt; for(int i = 1; i < n; i++) { int x, y; cin >> x >> y; add(x, y); add(y, x); } cnt = 1; a[1] = rt; pos[rt] = 1; dfs(rt); // for(int i = 1; i <= n; i++) { // cout << size[i] << endl; // } build(1, 1, n); while(m--) { int opt, u; cin >> opt >> u; if(opt == 0 || opt == 1) { modify(1, 1, n, pos[u], pos[u] + size[u] - 1, opt); // cout << query(1, 1, n, pos[u], pos[u] + size[u] - 1) << endl; } else { cout << query(1, 1, n, pos[u], pos[u] + size[u] - 1) << endl; } } return 0; }
数组一定要开四倍大小
tag标记的是啥一定要想清楚
void update(int u, int ul, int ur, int x) { if(x == 0) sum[u] = ur - ul + 1; else sum[u] = 0; tag[u] = x; } void pushdown(int u, int ul, int ur) { if(tag[u] != -1) { int mid = ul + ur >> 1; update(u << 1, ul, mid, tag[u]); update(u << 1 | 1, mid + 1, ur, tag[u]); tag[u] = -1; } }
特别是这两处tag的更新