CF620E New Year Tree 題解
阿新 • • 發佈:2021-10-30
Description
Solution
本題要不難想到要用線段樹或樹狀陣列之類的資料結構,但是題目要求在樹上操作,我們該如何處理呢?
首先我們要用到一個叫dfs序的概念。其實並不難,剛接觸的同學不要被它嚇到,它本質上就是一棵樹的先序遍歷,所謂先序遍歷就是先遍歷根,然後遍歷左子節點,最後遍歷右子節點。我們需要把dfs序存在pos陣列中,並把每個節點第一次遍歷到的時間點和第二次遍歷到的時間點存到in和out陣列中,這樣就成功地把一棵樹轉換為了線性結構。對一棵子樹進行操作時,只需對這棵子樹的根節點兩次遍歷到的時間戳中間的部分進行操作即可。
求dfs序的程式碼:
inline void dfs(int x, int fa){ in[x] = ++tim, pos[tim] = x; for(int i = head[x]; i; i = edge[i].nxt){ int y = edge[i].v; if(y != fa) dfs(y, x); } out[x] = tim; }
然後我們就可以用dfs序,也就是pos陣列對線段樹進行操作了,不過需要用到狀態壓縮,要把顏色壓縮成二進位制數到線段樹中,所以要開long long。接下來基本上都是線段樹區間修改,區間查詢的模板了。需要注意的是,查詢出來的值是一個經過狀壓後的數,我們需要把它分解。這裡可以借鑑樹狀陣列的思想,即每次減去一個lowbit(一棵樹上有數值的節點的最低位,不會的話可以先去學習一下樹狀陣列,這裡不再過多贅述)再讓ans++,因為狀壓後只有0和1,有值的話一定是1。ans就是最後的答案。
Code
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #define ls rt << 1 #define rs rt << 1 | 1 #define ll long long using namespace std; inline int read(){ int x = 0; char ch = getchar(); while(ch < '0' || ch > '9') ch = getchar(); while(ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar(); return x; } const int N = 4e5 + 10; int n, m; int a[N]; struct node{ int v, nxt; }edge[N << 1]; int head[N], tot; int in[N], out[N], pos[N], tim; inline void add(int x, int y){ edge[++tot] = (node){y, head[x]}; head[x] = tot; } inline void dfs(int x, int fa){ in[x] = ++tim, pos[tim] = x; for(int i = head[x]; i; i = edge[i].nxt){ int y = edge[i].v; if(y != fa) dfs(y, x); } out[x] = tim; } struct Seg_tree{ ll sum, cov; }t[N << 2]; inline void pushup(int rt){ t[rt].sum = t[ls].sum | t[rs].sum; } inline void pushdown(int rt){ if(t[rt].cov){ t[ls].sum = t[rs].sum = t[rt].cov; t[ls].cov = t[rs].cov = t[rt].cov; t[rt].cov = 0; } } inline void build(int l, int r, int rt){ if(l == r){ t[rt].sum = (1ll << a[pos[l]]); return; } int mid = (l + r) >> 1; build(l, mid, ls); build(mid + 1, r, rs); pushup(rt); } inline void update(int L, int R, int k, int l, int r, int rt){ if(L <= l && r <= R){ t[rt].sum = (1ll << k); t[rt].cov = (1ll << k); return; } pushdown(rt); int mid = (l + r) >> 1; if(L <= mid) update(L, R, k, l, mid, ls); if(R > mid) update(L, R, k, mid + 1, r, rs); pushup(rt); } inline ll query(int L, int R, int l, int r, int rt){ if(L <= l && r <= R) return t[rt].sum; pushdown(rt); int mid = (l + r) >> 1; ll res = 0; if(L <= mid) res |= query(L, R, l, mid, ls); if(R > mid) res |= query(L, R, mid + 1, r, rs); return res; } inline int calc(ll x){ int res = 0; for(; x; x -= x & (-x)) res++; return res; } int main(){ n = read(), m = read(); for(int i = 1; i <= n; ++i) a[i] = read(); for(int i = 1; i < n; ++i){ int u = read(), v = read(); add(u, v), add(v, u);; } dfs(1, 0); build(1, n, 1); for(int i = 1; i <= m; ++i){ int op = read(), x = read(); if(op == 1){ int c = read(); update(in[x], out[x], c, 1, n, 1); }else printf("%d\n", calc(query(in[x], out[x], 1, n, 1))); } return 0; }
End
本文來自部落格園,作者:xixike,轉載請註明原文連結:https://www.cnblogs.com/xixike/p/15484369.html