【树链剖分】【线段树】树的统计(金牌导航 树链剖分-1)
生活随笔
收集整理的這篇文章主要介紹了
【树链剖分】【线段树】树的统计(金牌导航 树链剖分-1)
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
樹(shù)的統(tǒng)計(jì)
金牌導(dǎo)航 樹(shù)鏈剖分-1
題目大意
給出一棵樹(shù),讓你做若干操作,操作如下:
1.修改一個(gè)節(jié)點(diǎn)的值
2.查詢兩個(gè)節(jié)點(diǎn)之間路徑的最大值
3.查詢兩個(gè)節(jié)點(diǎn)之間路徑的和
輸入樣例
4 1 2 2 3 4 1 4 2 1 3 12 QMAX 3 4 QMAX 3 3 QMAX 3 2 QMAX 2 3 QSUM 3 4 QSUM 2 1 CHANGE 1 5 QMAX 3 4 CHANGE 3 6 QMAX 3 4 QMAX 2 4 QSUM 3 4輸出樣例
4 1 2 2 10 6 5 6 5 16數(shù)據(jù)范圍
1?N?3×104,0?q?2×105,?3×104?si?3×1041\leqslant N\leqslant 3\times 10^4,0\leqslant q\leqslant 2\times10^5,-3\times 10^4\leqslant s_i\leqslant 3\times 10^41?N?3×104,0?q?2×105,?3×104?si??3×104
解題思路
樹(shù)鏈剖分,然后用線段樹(shù)維護(hù)重鏈,每個(gè)節(jié)點(diǎn)維護(hù)最大值和權(quán)值和
代碼
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define ll long long #define N 30030 using namespace std; int n, m, x, y, w, tot, ansmax, anssum; int a[N], s[N<<4], v[N], fa[N], hs[N], han[N], dfn[N], dep[N], size[N], head[N], maxx[N<<4]; string str; struct rec {int to, next; }e[N<<1]; void add(int x, int y) {e[++tot].to = y;e[tot].next = head[x];head[x] = tot; } void dfs1(int x)//找重兒子 {size[x] = 1;for (int i = head[x]; i; i = e[i].next)if (e[i].to != fa[x]){fa[e[i].to] = x;dep[e[i].to] = dep[x] + 1;dfs1(e[i].to);size[x] += size[e[i].to];if (size[e[i].to] > size[hs[x]]) hs[x] = e[i].to;}return; } void dfs2(int x) {dfn[x] = ++w;v[w] = x;if (hs[x]){han[hs[x]] = han[x];//重祖先dfs2(hs[x]);}for (int i = head[x]; i; i = e[i].next)if (e[i].to != fa[x] && e[i].to != hs[x]){han[e[i].to] = e[i].to;dfs2(e[i].to);} } void up(int x) {s[x] = s[x * 2] + s[x * 2 + 1];maxx[x] = max(maxx[x * 2], maxx[x * 2 + 1]);return; } void build(int now, int l, int r)//線段樹(shù)維護(hù) {if (l == r){maxx[now] = s[now] = a[v[l]];return;}int mid = (l + r) >> 1;build(now * 2, l, mid);build(now * 2 + 1, mid + 1, r);up(now);return; } void change(int x, int y, int now, int l, int r) {if (l == r){maxx[now] = s[now] = y;return;}int mid = (l + r) >> 1;if (x <= mid) change(x, y, now * 2, l, mid);else change(x, y, now * 2 + 1, mid + 1, r);up(now);return; } void ask(int now, int ql, int qr, int l, int r) {if (l == ql && r == qr){ansmax = max(ansmax, maxx[now]);anssum += s[now];return;}int mid = (l + r) >> 1;if (qr <= mid) {ask(now * 2, ql, qr, l, mid); return;}if (ql > mid) {ask(now * 2 + 1, ql, qr, mid + 1, r); return;}ask(now * 2, ql, mid, l, mid);ask(now * 2 + 1, mid + 1, qr, mid + 1, r);return; } void askk(int x, int y)//計(jì)算路徑長(zhǎng)度 {anssum = 0;ansmax = -N;while(han[x] != han[y]){if (dep[han[x]] < dep[han[y]]) swap(x, y);ask(1, dfn[han[x]], dfn[x], 1, n);x = fa[han[x]];}if (dep[x] < dep[y]) swap(x, y);ask(1, dfn[y], dfn[x], 1, n);return; } int main() {scanf("%d", &n);for (int i = 1; i < n; ++i){scanf("%d%d", &x, &y);add(x, y);add(y, x);}for (int i = 1; i <= n; ++i)scanf("%d", &a[i]);fa[1] = 1;han[1] = 1;dfs1(1);dfs2(1);build(1, 1, n);scanf("%d", &m);while(m--){cin>>str;scanf("%d%d", &x, &y);if (str == "CHANGE"){change(dfn[x], y, 1, 1, n);}else{askk(x, y);if (str == "QSUM") printf("%d\n", anssum);else if (str == "QMAX") printf("%d\n", ansmax);}}return 0; }總結(jié)
以上是生活随笔為你收集整理的【树链剖分】【线段树】树的统计(金牌导航 树链剖分-1)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 【FHQ treap】维护书架(金牌导航
- 下一篇: 【点分治】Tree(luogu 4178