线段树合并与分解
合并
#include <bits/stdc++.h>
using namespace std;
#define asd(i,a,b) for(int i=a;i<=b;i++)
#define int long long
const int inf = 0x3f3f3f3f, N = 1e5 + 5, Z = 1e5;
int n, m, fa[N], o[N][25], dep[N], tot, root[N], ans[N];
vector<int> g[N];
struct node {int l, r, sum, res;
} t[N * 50];void pushup(int o) {if (t[t[o].l].sum < t[t[o].r].sum) {t[o].res = t[t[o].r].res;t[o].sum = t[t[o].r].sum;}else {t[o].res = t[t[o].l].res;t[o].sum = t[t[o].l].sum;}
}void update(int& o, int l, int r, int pos, int val) {if (!o) o = ++tot;if (l == r) {t[o].sum += val;t[o].res = l;return;}int mid = (l + r) >> 1;if (pos <= mid) update(t[o].l, l, mid, pos, val);else update(t[o].r, mid + 1, r, pos, val);pushup(o);
}void merge(int& x, int y, int l, int r) {if (!x || !y) {x = x ? x : y;return;}if (l == r) {t[x].sum += t[y].sum;if (t[x].sum > 0) t[x].res = l;else t[x].res = 0;return;}int mid = (l + r) >> 1;merge(t[x].l, t[y].l, l, mid);merge(t[x].r, t[y].r, mid + 1, r);pushup(x);
}void init(int x, int pre) {o[x][0] = pre;dep[x] = dep[pre] + 1;for (int i = 1; i <= 20; i++) {o[x][i] = o[o[x][i - 1]][i - 1];}for (int y : g[x]) {if (y == pre) continue;init(y, x);}
}int lca(int x, int y) {if (dep[x] < dep[y]) swap(x, y);for (int i = 20; i >= 0; i--) {if (dep[o[x][i]] >= dep[y]) x = o[x][i];}if (x == y) return x;for (int i = 20; i >= 0; i--) {if (o[x][i] != o[y][i]) {x = o[x][i];y = o[y][i];}}return o[x][0];
}void dfs(int x, int fa) {for (int y : g[x]) {if (y == fa) continue;dfs(y, x);merge(root[x], root[y], 1, Z);}ans[x] = t[root[x]].res;
}signed main() {ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);cin >> n >> m;asd(i, 1, n - 1) {int u, v; cin >> u >> v;g[u].push_back(v);g[v].push_back(u);}init(1, 0);asd(i, 1, m) {int a, b, c; cin >> a >> b >> c;int ut = lca(a, b);update(root[a], 1, Z, c, 1);update(root[b], 1, Z, c, 1);update(root[ut], 1, Z, c, -1);update(root[o[ut][0]], 1, Z, c, -1);}dfs(1, 0);asd(i, 1, n) cout << ans[i] << '\n';return 0;
}
分裂
P5494 【模板】线段树分裂
题目描述
给出一个可重集 a a a(编号为 1 1 1),它支持以下操作:
0 p x y
:将可重集 p p p 中大于等于 x x x 且小于等于 y y y 的值移动到一个新的可重集中(新可重集编号为从 2 2 2 开始的正整数,是上一次产生的新可重集的编号+1)。
1 p t
:将可重集 t t t 中的数放入可重集 p p p,且清空可重集 t t t(数据保证在此后的操作中不会出现可重集 t t t)。
2 p x q
:在 p p p 这个可重集中加入 x x x 个数字 q q q。
3 p x y
:查询可重集 p p p 中大于等于 x x x 且小于等于 y y y 的值的个数。
4 p k
:查询在 p p p 这个可重集中第 k k k 小的数,不存在时输出 -1
。
输入格式
第一行两个整数 n , m n,m n,m,表示可重集中的数在 1 ∼ n 1\sim n 1∼n 的范围内,有 m m m 个操作。
接下来一行 n n n 个整数,表示 1 ∼ n 1 \sim n 1∼n 这些数在 a a a 中出现的次数 ( a i ≤ m ) (a_{i} \leq m) (ai≤m)。
接下来的 m m m 行每行若干个整数,第一个数为操作的编号 o p t opt opt( 0 ≤ o p t ≤ 4 0 \leq opt \leq 4 0≤opt≤4),以题目描述为准。
输出格式
依次输出每个查询操作的答案。
#include <bits/stdc++.h>
using namespace std;
#define asd(i,a,b) for(int i=a;i<=b;i++)
#define int long long
constexpr int N = 2e5 + 10;
int n, m, idx = 1, cnt, tot;
int sum[N << 5], ls[N << 5], rs[N << 5], root[N << 2], rub[N << 5];int New() { return cnt ? rub[cnt--] : ++tot; }
void Del(int& p) {ls[p] = rs[p] = sum[p] = 0;rub[++cnt] = p;p = 0;
}void push_up(int p) { sum[p] = sum[ls[p]] + sum[rs[p]]; }void build(int s, int t, int& p) {if (!p) p = New();if (s == t) {cin >> sum[p];return;}int m = (s + t) >> 1;build(s, m, ls[p]);build(m + 1, t, rs[p]);push_up(p);
}void update(int x, int c, int s, int t, int& p) {if (!p) p = New();if (s == t) {sum[p] += c;return;}int m = (s + t) >> 1;if (x <= m) update(x, c, s, m, ls[p]);else update(x, c, m + 1, t, rs[p]);push_up(p);
}int merge(int p, int q, int s, int t) {if (!p || !q) return p | q;if (s == t) {sum[p] += sum[q];Del(q);return p;}int m = (s + t) >> 1;ls[p] = merge(ls[p], ls[q], s, m);rs[p] = merge(rs[p], rs[q], m + 1, t);push_up(p);Del(q);return p;
}void split(int& p, int& q, int s, int t, int l, int r) {if (t < l || r < s) return;if (!p) return;if (l <= s && t <= r) {q = p;p = 0;return;}if (!q) q = New();int m = (s + t) >> 1;if (l <= m) split(ls[p], ls[q], s, m, l, r);if (m < r) split(rs[p], rs[q], m + 1, t, l, r);push_up(p);push_up(q);
}int query(int l, int r, int s, int t, int p) {if (!p) return 0;if (l <= s && t <= r) return sum[p];int m = (s + t) >> 1;int ans = 0;if (l <= m) ans += query(l, r, s, m, ls[p]);if (m < r) ans += query(l, r, m + 1, t, rs[p]);return ans;
}int kth(int s, int t, int k, int p) {if (s == t) return s;int m = (s + t) >> 1;int left = sum[ls[p]];if (k <= left) return kth(s, m, k, ls[p]);else return kth(m + 1, t, k - left, rs[p]);
}signed main() {ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);cin >> n >> m;build(1, n, root[1]);while (m--) {int op, p, x, y, q, k;cin >> op;switch (op) {case 0:cin >> p >> x >> y;split(root[p], root[++idx], 1, n, x, y);break;case 1:cin >> p >> q;root[p] = merge(root[p], root[q], 1, n);break;case 2:cin >> p >> x >> q;update(q, x, 1, n, root[p]);break;case 3:cin >> p >> x >> y;cout << query(x, y, 1, n, root[p]) << '\n';break;case 4:cin >> p >> k;if (sum[root[p]] < k) cout << "-1\n";else cout << kth(1, n, k, root[p]) << '\n';break;}}return 0;
}