P3384 輕重鏈剖分
阿新 • • 發佈:2020-07-21
複雜資料結構++
樹鏈剖分,極大地提高了樹上元素的查詢修改的效率。
這裡是輕重鏈剖分,通過將對樹的dfs,在求出每個節點的父親,深度,子樹大小,重兒子編號的同時,把樹分割成了若干條重鏈,每條鏈都有獨一無二的top節點
然後將這些鏈逐一標號對映到序列中,在輕重鏈剖分下每一個節點及其子樹的下表在序列中連續。
因此就可以通過線段樹之類的資料結構在log級別的時間內修改查詢
#pragma GCC optimize(2) #include<bits/stdc++.h> #define ll long long #define fastio ios::sync_with_stdio(false);cin.tie(NULL);cout.tie(NULL); using namespace std; double pi = acos(-1); const double eps = 1e-12; const int maxn = 1e5 + 10; const int inf = 1e9; int read() { int x = 0, f = 1; char c = getchar(); while (c < '0' || c>'9') { if (c == '-') f = -1; c = getchar(); } while (c >= '0' && c <= '9') x = (x << 1) + (x << 3) + c - '0', c = getchar(); return f * x; } ll a[maxn], wt[maxn], mod; int n, m, S; int head[maxn], deep[maxn], fa[maxn], siz[maxn], son[maxn], id[maxn], top[maxn], edge_cnt = 0, cnt = 0; struct edge { int to, next; }e[2 * maxn]; inline void add(int from, int to) { e[++edge_cnt].to = to; e[edge_cnt].next = head[from]; head[from] = edge_cnt; } void dfs1(int now, int f, int dee)//先記錄每個點的深度,子樹大小,父節點編號,重兒子編號 { deep[now] = dee; fa[now] = f; siz[now] = 1; int maxson = -1; for (int i = head[now]; i; i = e[i].next) { int to = e[i].to; if (to == f)continue; dfs1(to, now, dee + 1); siz[now] += siz[to]; if (siz[to] > maxson)son[now] = to, maxson = siz[to]; } } void dfs2(int now, int topf) { id[now] = ++cnt; wt[cnt] = a[now];//將節點值對映到序列 top[now] = topf; if (!son[now])return; dfs2(son[now], topf);//先處理重兒子 for (int i = head[now]; i; i = e[i].next) { int to = e[i].to; if (to == fa[now] || to == son[now])continue; dfs2(to, to);//遍歷輕兒子,然而輕兒子還是會先遍歷重邊,所以每一條重鏈新編號是連續的 } //最終子樹的編號也是連續的,因此可以用線段樹查詢子樹和 } struct tree { int ls; int rs; long long vue = 0; long long lazy = 0; }t[4 * maxn]; void build(int l, int r, int p) { t[p].ls = l; t[p].rs = r; if (l == r) { t[p].vue = wt[l] % mod; return; } int mid = l + r >> 1; build(l, mid, p << 1); build(mid + 1, r, (p << 1) + 1); t[p].vue = (t[p << 1].vue + t[(p << 1) + 1].vue) % mod; } inline void spread(int x)//標記下傳 { if (t[x].lazy)//如果該節點標記不為0 { t[x * 2].vue += t[x].lazy * (ll)(t[x * 2].rs - t[x * 2].ls + 1);//更新左子區間和 t[x * 2 + 1].vue += t[x].lazy * (ll)(t[x * 2 + 1].rs - t[x * 2 + 1].ls + 1);//更新右子區間和 t[x * 2].lazy += t[x].lazy; t[x * 2 + 1].lazy += t[x].lazy;//向子節點傳遞標記 t[x].lazy = 0;//初始化標記 } } void update_add(int x, int y, ll v, int p) { ll l = t[p].ls; ll r = t[p].rs; if (l >= x && r <= y) { t[p].vue += (long long)v * (r - l + 1); t[p].lazy += v; return; } spread(p); int mid = l + r >> 1; if (x <= mid)update_add(x, y, v, p << 1); if (y > mid)update_add(x, y, v, (p << 1) + 1); t[p].vue = t[p << 1].vue + t[(p << 1) + 1].vue; } long long query(int x, int y, int p) { int l = t[p].ls; int r = t[p].rs; if (l >= x && r <= y) { return t[p].vue % mod; } int mid = l + r >> 1; long long ans = 0; spread(p); if (x <= mid)ans = (ans + query(x, y, p << 1)) % mod; if (y > mid)ans = (ans + query(x, y, (p << 1) + 1)) % mod; return ans; } void update_range(int x, int y,ll v) { v %= mod; while (top[x] != top[y]) { if (deep[top[y]] > deep[top[x]])swap(x, y); update_add(id[top[x]], id[x], v, 1); x = fa[top[x]]; } if (deep[x] > deep[y])swap(x, y); update_add(id[x], id[y], v, 1); } ll query_range(int x,int y) { ll res = 0; while (top[x] != top[y]) { if (deep[top[y]] > deep[top[x]])swap(x, y); res = (res + query(id[top[x]], id[x], 1)) % mod; x = fa[top[x]]; } if (deep[x] > deep[y])swap(x, y); res = (res + query(id[x], id[y], 1)) % mod; return res; } int main() { fastio; cin >> n >> m >> S >> mod; for (int i = 1; i <= n; i++) cin >> a[i]; for (int i = 1; i < n; i++) { int x, y; cin >> x >> y; add(x, y); add(y, x); } dfs1(S, 0, 1); dfs2(S, S); build(1, n, 1); ll o, x, y, z; while (m--) { cin >> o; if (o == 1) { cin >> x >> y >> z; update_range(x, y, z); } else if (o == 2) { cin >> x >> y; cout << query_range(x, y) << endl; } else if (o == 3) { cin >> x >> z; update_add(id[x], id[x] + siz[x] - 1, z, 1); } else { cin >> x; cout << query(id[x], id[x] + siz[x] - 1, 1) << endl; } } return 0; }