voidupdate(int &rt, int L, int R, ll val, ll y){ //别忘了加取址符 if (!rt) rt = ++idx; if (L == R) { tree[rt].cnt += y; return; } ll mid = L + R >> 1; if (val <= mid) update(tree[rt].l, L, mid, val, y); elseupdate(tree[rt].r, mid + 1, R, val, y); pushup(rt); }
ll query(int rt, int L, int R, int l, int r){ if (L >= l && R <= r) return tree[rt].cnt; int mid = L + R >> 1; ll ans = 0; if (mid + 1 <= r) ans += query(tree[rt].r, mid + 1, R, l, r); if (l <= mid) ans += query(tree[rt].l, L, mid, l, r); return ans; }
voidsolve(){ n = read(); ll ans = 0; for (int i = 1; i <= n; i++) { int num = read(); ans += query(rt, LL, RR, num + 1, RR); update(rt, LL, RR, num, 1); } printf("%lld\n", ans); }
voidupdate(int &rt, int L, int R, int val, int y){ //L,R表示区间信息,一定要加引用 if (!rt) { rt = ++ idx; tree[rt] = {0, 0, 0}; } if (L == R) { tree[rt].cnt += y; if (tree[rt].cnt < 0) tree[rt].cnt = 0; return; } int mid = L + R >> 1; if (val <= mid) update(tree[rt].l, L, mid, val, y); if (mid + 1 <= val) update(tree[rt].r, mid + 1, R, val, y); pushup(rt); }
intquery(int rt, int L, int R, int l, int r){ //L,R表示区间信息,查询x的排名是多少 if (!rt) return0; if (L >= l && R <= r) return tree[rt].cnt; int mid = L + R >> 1; int ans = 0; if (l <= mid) ans += query(tree[rt].l, L, mid, l, r); if (mid + 1 <= r) ans += query(tree[rt].r, mid + 1, R, l, r); return ans; }
intkth(int rt, int L, int R, int k){ //查询第k小的数是什么 if (L == R) return L; int mid = L + R >> 1; if (tree[tree[rt].l].cnt >= k) returnkth(tree[rt].l, L, mid, k); elsereturnkth(tree[rt].r, mid + 1, R, k - tree[tree[rt].l].cnt); }
intgetpre(int rt, int x){ //查找小于x的最大值 int rk = query(1, LL, RR, LL, x - 1 + base); returnkth(1, LL, RR, rk) - base; }
intgetnxt(int rt, int x){ //查找大于x的最小值 int rk = query(1, LL, RR, LL, x + base) + 1; returnkth(1, LL, RR, rk) - base; }
voidsolve(){ n = read(); //输入有负数的存在 for (int i = 1; i <= n; i++) { int opt, x; opt = read(), x = read(); int rt = 1; if (opt == 1) // 插入一个数 update(rt, LL, RR, x + base, 1); elseif (opt == 2) //删除一个数 update(rt, LL, RR, x + base, -1); elseif (opt == 3) //查询数值为x的数的排名 printf("%d\n", query(1, LL, RR, LL, base + x - 1) + 1); elseif (opt == 4) //查询排名为x的数的数值大小 printf("%d\n", kth(1, LL, RR, x) - base); elseif (opt == 5) //查找小于x的最大数 printf("%d\n", getpre(1, x)); elseif (opt == 6) //查找大于x的最小数 printf("%d\n", getnxt(1, x)); } }
voidbuild(int rt, int l, int r){ tree[rt].l = l, tree[rt].r = r; if (l == r) { tree[rt].sum = a[l]; tree[rt].mxopt = a[l]; return; } int mid = l + r >> 1; build(rt << 1, l, mid); build(rt << 1 | 1, mid + 1, r); pushup(rt); }
voidupdate(int rt, int l, int r){ if (tree[rt].l == tree[rt].r) { //看似是单点更新,但根据更新上限小的特点时间复杂度说得过去 tree[rt].sum = (ll)(sqrt(tree[rt].sum)); tree[rt].mxopt = (ll)(sqrt(tree[rt].mxopt)); return; } int mid = tree[rt].l + tree[rt].r >> 1; if (mid >= l && tree[rt << 1].mxopt > 1) update(rt << 1, l, r); if (mid + 1 <= r && tree[rt << 1 | 1].mxopt > 1) update(rt << 1 | 1, l, r); pushup(rt); }
ll query(int rt, int l, int r){ if (tree[rt].l >= l && tree[rt].r <= r) return tree[rt].sum; ll ans = 0; int mid = tree[rt].l + tree[rt].r >> 1; if (mid >= l) ans += query(rt << 1, l, r); if (mid + 1 <= r) ans += query(rt << 1 | 1, l, r); return ans; }
voidsolve(){ n = read(); for (int i = 1; i <= n; i ++) a[i] = read(); build(1, 1, n); int m; m = read(); while (m--) { int opt, l, r; opt = read(), l = read(), r = read(); if (l > r) swap(l, r); if (opt == 0) update(1, l, r); //区间开方 elseprintf("%lld\n", query(1, l, r)); //区间求和 } }
// 去重后y轴上一共有siz个节点,siz - 1个区间,对这些区间建树 int siz = ys.size(); build(1, 0, siz - 2); double ans = 0; for (int i = 1; i <= 2 * n; i ++) { //扫描线扫过去 if (i > 1) ans += tree[1].len * (seg[i].x - seg[i - 1].x); update(1, findx(seg[i].y1), findx(seg[i].y2) - 1, seg[i].k);
以求区间第 k 小值为例题。已知利用权值线段树可以求出整个区间的第 k 小数,可以利用可持久化权值线段树分别求得 [1,l−1] 和 [1,r] 的前缀和。前者可以求出插入 l−1 个数,也就是第 l−1 个版本下区间的第 k 小值,后者同理。他们的差就是这中间插入的数的总和,利用这个总和就可以求得区间 k 小值。如果左子树的数量大于等于 k, 就递归访问左子树,否则就递归访问右子树。
vector <int> num; //离散化用 intfindx(int x){returnlower_bound(num.begin(), num.end(), x) - num.begin() + 1;} //离散化用 int n, m; //n个数据, m组询问 int a[MAXN]; //存放原始数据和离散化后结果 int rt[MAXN], idx; //不同的版本入口 structnode { int l, r, cnt; }tree[(MAXN << 2) + MAXN * 17]; //空间复杂度 O(原本size + nlog2n)
intbuild(int l, int r){ int now = ++idx; int mid = l + r >> 1; if (l < r) { tree[now].l = build(l, mid); tree[now].r = build(mid + 1, r); } tree[now].cnt = 0; return now; }
intupdate(int pre, int L, int R, int k){ //上一个版本变化的部分全部裂开成一条新的链,其他不变的继承上一个版本 int now = ++idx; int mid = L + R >> 1; tree[now].l = tree[pre].l, tree[now].r = tree[pre].r, tree[now].cnt = tree[pre].cnt + 1;//继承 if (L < R) { if (k <= mid) tree[now].l = update(tree[pre].l, L, mid, k); //裂开 elseif (mid + 1 <= k) tree[now].r = update(tree[pre].r, mid + 1, R, k); //裂开 } return now; }
intquery(int l, int r, int L, int R, int k){ //sum[r] - sum[l] 即为区间第k大值 if (L == R) return L; //找到了这个数 int mid = L + R >> 1; int sum = tree[tree[r].l].cnt - tree[tree[l].l].cnt; //这段区间的左部分插入了sum个数 if (sum >= k) returnquery(tree[l].l, tree[r].l, L, mid, k); else returnquery(tree[l].r, tree[r].r, mid + 1, R, k - sum); }
voidsolve(){ n = read(), m = read(); for (int i = 1; i <= n; i ++) a[i] = read(),num.pb(a[i]); sort(num.begin(), num.end()); num.erase(unique(num.begin(), num.end()), num.end()); for (int i = 1 ; i <= n; i++) a[i] = findx(a[i]); int len = num.size(); rt[0] = build(1, len); for (int i = 1; i <= n; i++) rt[i] = update(rt[i-1], 1, len, a[i]); while (m--) { //询问[l,r]这个区间的第k小数 int l, r, k; l = read(), r = read(), k = read(); int ans = query(rt[l - 1], rt[r], 1, len, k); printf("%d\n", num[ans - 1]); } }
平衡树(treap实现)
顾名思义,treap=tree+heap ,其中 tree 指的是二叉搜索树,heap 指的是大根堆。这种数据结构要求每个节点拥有两种权值,其中一种满足二叉搜索树的性质,另外一种满足大根堆的性质。这样做是为了让整个二叉搜索树尽可能随机,防止二叉搜索树向链的方向演变。
int n; //节点数量 int opt, x; int root, idx; //有些操作要引用,定义root;idx就相当于链表操作 structnode { //存放平衡树中的节点信息 int l, r, cnt; //左右儿子编号,当前数出现次数 int key, val; //BST和大根堆的权值 int size; //子树大小 }tree[MAXN];
voidupdate_path(int u, int v, int add){ while (top[u] != top[v]) { //寻找最近公共重链 if (depth[top[u]] < depth[top[v]]) swap(u, v); update(1, key[top[u]], key[u], add); u = fa[top[u]]; } if (depth[u] < depth[v]) swap(u, v); update(1, key[v], key[u], add); //公共重链上别忘了更新区间信息 }
ll query_path(int u, int v){ ll ans = 0; while (top[u] != top[v]) { //寻找最近公共重链 if (depth[top[u]] < depth[top[v]]) swap(u, v); ans += query(1, key[top[u]], key[u]); u = fa[top[u]]; } if (depth[u] < depth[v]) swap(u, v); ans += query(1, key[v], key[u]); //公共重链上别忘了计算区间贡献 return ans; }
voidsolve(){ n = read(); for (int i = 1; i <= n; i++) val[i] = read(); for (int i = 1; i < n; i++) { int u, v; u = read(), v = read(); add_edge(u, v); add_edge(v, u); } dfs1(1, 1, -1); dfs2(1, 1); build(1, 1, n); int k; k = read(); for (int i = 1; i <= k; i++) { int opt; int u, v, add; opt = read(), u = read(); if (opt == 1) { //修改路径节点权值 v = read(), add = read(); update_path(u, v, add); } elseif (opt == 2) { //修改子树节点权值 add = read(); update_tree(u, add); } elseif(opt == 3) { //查询路径节点权值和 v = read(); printf("%lld\n", query_path(u, v)); } else//查询子树节点权值和 printf("%lld\n", query_tree(u)); } }
voidupdate(int l, int r, int d){ if (get(l) == get(r)) { //段内直接暴力 for (int i = l; i <= r; i ++) a[i] += d, sum[get(i)] += d; } else { int i = l, j = r; while (get(i) == get(l)) sum[get(i)] += d, a[i] += d, i ++; while (get(j) == get(r)) sum[get(j)] += d, a[j] += d, j --; for (int k = get(i); k <= get(j); k ++) sum[k] += d * len, add[k] += d; } }
ll query(int l, int r){ ll ans = 0; if (get(l) == get(r)) { //段内直接暴力 for (int i = l; i <= r; i ++) ans += a[i] + add[get(i)]; } else { int i = l, j = r; while (get(i) == get(l)) ans += a[i] + add[get(i)], i ++; while (get(j) == get(r)) ans += a[j] + add[get(j)], j --; for (int k = get(i); k <= get(j); k ++) ans += sum[k]; } return ans; }
voidsolve(){ n = read(), m = read(); len = sqrt(n); //分块 for (int i = 1; i <= n; i ++) a[i] = read(), sum[get(i)] += a[i]; while (m--) { scanf("%s%d%d",opt, &l, &r); if (opt[0] == 'C') { d = read(); update(l, r, d); } else printf("%lld\n", query(l, r)); } }
#define get(x) (x / len) usingnamespace std; constint N = 5e4 + 5, M = 2e5 + 5, S = 1e6 + 5; int n, m, a[N], ans[M], cnt[S], len; structQuery { int id, l, r; } q[M]; boolcmp(Query a, Query b){ int i = get(a.l), j = get(b.l); if (i != j) return i < j; return a.r < b.r; } voidadd(int x, int &res){ if (!cnt[x]) res ++; cnt[x] ++; } voiddel(int x, int &res){ cnt[x] --; if (!cnt[x]) res --; } signedmain(){ scanf("%d", &n); for (int i = 1; i <= n; i ++) scanf("%d", &a[i]); scanf("%d", &m); len = sqrt((double)n * n / m); for (int i = 0; i < m; i ++) scanf("%d%d", &q[i].l, &q[i].r), q[i].id = i; sort(q, q + m, cmp); for (int k = 0, i = 0, j = 1, res = 0; k < m; k ++) { int id = q[k].id, l = q[k].l, r = q[k].r; while (i < r) add(a[++ i], res); while (i > r) del(a[i --], res); while (j > l) add(a[-- j], res); while (j < l) del(a[j ++], res); ans[id] = res; } for (int i = 0; i < m; i ++) printf("%d\n", ans[i]); }