cf877 E. Danil and a Part-time Job(dfs序,線段樹)
阿新 • • 發佈:2022-01-03
題意:
一棵點權為0/1的樹,實現兩種操作:反轉 u-子樹 中的所有數、查詢 u-子樹 中1的數量
思路:
一次dfs求出三個陣列 dfn、L、R。dfn為原陣列按dfs序排列,L為節點 i 的dfs先序,R為節點 i 的一種奇怪的dfs後序。每個節點的R與它的最右邊的子節點的R相同。
相當於把原陣列按dfs先序重新排序,\([L(u),R(u)]\) 就是 u-子樹
對於每次修改把sum變成區間長度減sum即可
#include <bits/stdc++.h> using namespace std; const int N = 2e5 + 5; int n, q, val[N]; int h[N], e[N], ne[N], idx; void add(int a, int b) { e[++idx] = b, ne[idx] = h[a], h[a] = idx; } int cnt, L[N], R[N], dfn[N]; void dfs(int u) { dfn[++cnt] = u; L[u] = cnt; for(int i = h[u]; i; i = ne[i]) dfs(e[i]); R[u] = cnt; } struct node { int l, r; int sum, tag; //tag=1表示左右子節點要變 } tr[N*4]; void pushup(int u) { tr[u].sum = tr[u<<1].sum + tr[u<<1|1].sum; } void pushdown(int u) { node &root = tr[u], &left = tr[u<<1], &right = tr[u<<1|1]; if(root.tag) //! { left.tag ^= 1, right.tag ^= 1; left.sum = left.r-left.l+1 - left.sum; right.sum = right.r-right.l+1 - right.sum; root.tag = 0; } } void build(int u, int l, int r) { if(l == r) tr[u] = {l, r, val[dfn[l]]}; //! else { tr[u] = {l, r}; int mid = l + r >> 1; build(u<<1, l, mid), build(u<<1|1, mid+1, r); pushup(u); } } void modify(int u, int l, int r) { if(tr[u].l >= l && tr[u].r <= r) //! { tr[u].tag ^= 1; tr[u].sum = tr[u].r-tr[u].l+1 - tr[u].sum; } else { pushdown(u); int mid = tr[u].l + tr[u].r >> 1; if(l <= mid) modify(u<<1, l, r); if(r > mid) modify(u<<1|1, l, r); pushup(u); } } int query(int u, int l, int r) { if(tr[u].l >= l && tr[u].r <= r) return tr[u].sum; pushdown(u); int res = 0, mid = tr[u].l + tr[u].r >> 1; if(l <= mid) res += query(u<<1, l, r); if(r > mid) res += query(u<<1|1, l, r); return res; } signed main() { scanf("%d", &n); for(int i = 2, p; i <= n; i++) scanf("%d", &p), add(p, i); for(int i = 1; i <= n; i++) scanf("%d", &val[i]); dfs(1); build(1, 1, n); scanf("%d", &q); while(q--) { char op[5]; int x; scanf("%s%d", op, &x); if(op[0] == 'g') printf("%d\n", query(1, L[x], R[x])); else modify(1, L[x], R[x]); } return 0; }