权值线段树

学习权值线段树之前要先学会:
线段树(废话)

权值线段树主要思想就是利用线段树维护一个序列的值。

定义:

权值数列:对一个数列 \(a\) 构造一个数列 \(b\) ,其中 \(b[i]\) 表示 \(a\) 中数值 \(i\) 出现的次数,就是 \(a[j] = i\)\(j\) 的个数,这样的数列 \(b\) 就叫 \(a\) 的权值数列。
权值线段树:对数列 \(b\) 构造出的线段树就是权值线段树。

因为权值线段树维护的是值域,所以它可能承受不了 \(1e9\) 那样的数据范围,因为有空间范围在那限制着你呀,但是处理较小的数据还是很方便的。

权值线段树可以应用于很多的计数问题,比如你要改变 \(a[i]\) ,让它的值从 \(x\) 变为 \(y\) ,你只需要在对 \(b\) 构造的权值线段树上两遍 \(logn\) 的查询就能够维护住当前所有数字的个数。

比如一道经典题:逆序对

这个题可以用归并做,还可以有其他很多方法,但是自我感觉权值线段树最好弄。

具体的怎么做,就是 \(O(N)\) 枚举序列,一个一个往权值线段树里放,每次往里放的时候查询当前比这个数大的个数,答案累加起来就行了。

普通的权值线段树可以维护数组第k大值

具体的,就是从根往下搜,只要左子树大小大于 \(k\) ,就往左边搜,否则就往右边搜。

代码就不展示了,懒得写,只要会线段树的大概都会写吧 \(qwq\),而且没什么用

但是想要查询历史战绩,权值线段树就蒙了,这时候我们需要可持久化线段树来给权值线段树加个 \(buff\)


在可持久化中,已经讲过了怎么给线段树加可持久化。

那么现在,要给权值线段树加可持久化也易如反掌~

因为权值线段树维护的是值域,所以可持久化线段树现在也要维护值域。

具体的怎么做:

首先,构建三个数组 \(A,B,C\)(其他任意名称均可),设要加持久化的数组为 \(B\)\(A\) 为某一已知序列,\(C\) 为辅助数组, \(A,B,C\) 起始值全部相同。

\(B\) 进行离散化,这时候 \(B\) 中所有值单调且唯一,所以我们可以找到 \(A\) 中每一个值的排名,在 \(B\) 数组中进行 \(lower_bound\) ,出来的数存到 \(C\) 中,就是
\(a\) 中每个数的排名。

建主席树,用离散化的数组B

建好之后将往里边加数字看做一次操作,像改变数组一样改变不断建主席树。

每次将第 \(i\) 个数加进去。

因为我们的 \(C\) 数组表示的是每个数的排名,所以往里边放数的时候直接传入排名,只要左子树大小大于排名,往左边搜,否则往右边搜。

一旦找到了那个数的位置, \(siz++\) ,因为每个叶子都代表的是一个值,所以 \(siz\) 增大表示这个值增多。

现在,我们的主席树算是建完了。

接下来就是查询某一区间第 \(k\) 值,这就简单了,找到区间右端点的那个位置对应的根,记为 \(now\) ,与左端点前一个位置对应的根,记为 \(las\) ,在往下找的过程中,我们需要用到两棵树的左右子树的大小,区间内对应数的多少就是 \(siz\) 之差。

所以在下搜过程中, \(siz[now] - siz[las]\) 就是这个区间内,各个数的个数。

知道了要相减就跟普通的查询没什么区别了。

以下代码中数组并不与上文中 \(A,B,C\) 数组相同。

\(c\) 表示原数组, \(a\) 表示离散化后每个数的排名, \(b\) 表示离散化数组, \(d\) 表示每个排名对应原数组中的数是什么。

\(code:\)

BOOM!!
/**
 *	author: zcxxxxx
 *	creater: 2022.6.8
**/
#include <bits/stdc++.h>
#define il inline
#define reg register
#define ll long long
//#define int unsigned long long
#define pii pair<int, int>
#define eps 1e-8
using namespace std;
const int A = 1e2 + 7;
const int B = 1e3 + 7;
const int C = 1e4 + 7;
const int D = 6e5 + 7;
const int E = 1e6 + 7;
const int F = 1e7 + 7;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + 7;
ll Gcd(ll num1, ll num2) {return !num2 ? num1 : Gcd(num2, num1 % num2);}
ll tx, ty, tz; void Exgcd(ll num1, ll num2) {if(num2 == 0) {tx = 1, ty = 0; return;}
	Exgcd(num2, num1 % num2), tz = tx, tx = ty, ty = tz - num1 / num2 * ty;}
/*General solution: tx += num2 / gcd * k, ty += num1 / gcd * k, k belong to Z*/
ll Gmul(ll x, ll y) {ll ans = 0; while(y != 0) {if(y & 1) ans = (ans + x) % mod; x = (x + x) % mod, y >>= 1;} return ans;}
ll Gpow(ll base, ll pow) {ll ans = 1;while(pow) {if(pow & 1) ans = Gmul(ans, base); base = Gmul(base, base);pow >>= 1;} return ans;}
ll Qpow(ll base, ll pow) {ll ans = 1;while(pow) {if(pow & 1) ans = (ans * base) % mod; base = (base * base) % mod;pow >>= 1;} return ans;}
inline int read() {register int x = 0, t = 1; register char ch = getchar();
while (ch != '-' && (ch < '0' || ch > '9')) ch = getchar(); if(ch == '-') {t = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') {x = x * 10 + ch - 48; ch = getchar();}return x * t;}
bool cmpstb(ll a, ll b) {return a < b;}
/*----------------------------------------*/
int n, m;
int cnt;
int a[D * 40], b[D * 40], c[D * 40], d[D * 40], root[D * 40];
struct node {
	int l, r, val, siz;
}t[D * 40];
void work() {
	sort(b + 1, b + 1 + n);
	int *tot = unique(b + 1, b + 1 + n);
	for(int i = 1; i <= n; ++ i) {
		a[i] = lower_bound(b + 1, tot, a[i]) - b;
		d[a[i]] = c[i];
	}
}
void build(int &now, int l, int r) {
	now = ++ cnt;
	if(l == r) {
		t[now].val = d[l];
		return;
	}
	int mid = l + r >> 1;
	build(t[now].l, l, mid);
	build(t[now].r, mid + 1, r);
}
void insert(int las, int &now, int l, int r, int x) {
	now = ++ cnt;
	t[now] = t[las];
	t[now].siz ++;
	if(l == r) return;
	int mid = l + r >> 1;
	if(x <= mid) insert(t[las].l, t[now].l, l, mid, x);
	else insert(t[las].r, t[now].r, mid + 1, r, x);
}
int query(int las, int now, int l, int r, int x) {
	if(l == r) return t[now].val;
	int k = t[t[now].l].siz - t[t[las].l].siz;
	int mid = l + r >> 1;
	if(x <= k) return query(t[las].l, t[now].l, l, mid, x);
	else return query(t[las].r, t[now].r, mid + 1, r, x - k);
}
int main() {
	n = read(), m = read();
	for(int i = 1; i <= n; ++ i) a[i] = b[i] = c[i] = read();
	work();
	build(root[0], 1, n);
	for(int i = 1; i <= n; ++ i) insert(root[i - 1], root[i], 1, n, a[i]);
	for(int x, y, k, i = 1; i <= m; ++ i) {
		x = read(), y = read(), k = read();
		cout << query(root[x - 1], root[y], 1, n, k) << "\n";
	}
	return 0;
}
posted @ 2022-06-08 10:38  zcxxxxx  阅读(218)  评论(0编辑  收藏  举报