主席树整理

主席树是一种奇怪的数据结构,好像又叫函数式线段树。主席树是一种可持久化数据结构,即它可以支持查询“历史记录”,比如区间第k大等等。现在先说静态的主席树

一、静态主席树

大致思路是:先将n元素的值离散为[0, sz-1],这棵树的每个节点记录的是在当前前缀[1...i]中,离散值处在[l, r]区间内的值的个数。

由线段树的单点更新可以知道,在树里更新或插入一个元素,只需要O(lgn)的时间复杂度,即只需更新一条链,主席树也是这样,每次更新一个新的[1...i],就和[1...i-1]比较,如果i的离散值在左儿子区间内,就只需新增左儿子,而可以与[1...i-1]公用右儿子,所以每次update只需新增lgn个节点,时间复杂度也是O(lgn)。

然后是查询,假设有一个查询(ql, qr, k)表示求在ql到qr区间内第k大的数。根据前缀和的思想,从root[qr]和root[ql-1]开始搜索,在这两个同样表示[l, r]区间的节点中,sum[root[qr]的子孙]-sum[root[ql-1]的子孙] = 位置在[ql, qr]区间内,值在[l, r]区间内的数的个数,如果当前点的左边区间sum值>=k,说明第k大在左儿子区间内,反之它在右儿子区间内,这样就可以递归求解了。

感谢这个博客殇雪的博客,代码非常好看而且易懂。感谢GJC大佬支持本文写作,感谢GZX,tiandong,turing,ZWZ围观本文写作。


上板子:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int N = 3e5+10;
int n, q, a[N];
vector<int> v;
int root[N], sum[N*40], lson[N*40], rson[N*40], tot, sz;
//最好开40倍空间,据实测20倍是不够的 

bool cmp(int x, int y){return x > y;}

int GETID(int x){return lower_bound(v.begin(), v.end(), x)-v.begin();}

void BUILD(int &u, int l, int r)
{
	u = ++tot;
	if (l == r) return;
	int mid = (l+r)>>1;
	BUILD(lson[u], l, mid);
	BUILD(rson[u], mid+1, r);
}

void UPDATE(int &u, int v, int l, int r, int x)
{
	u = ++tot;
	sum[u] = sum[v]+1;
	if (l == r) return;
	int mid = (l+r)>>1;
	if (x <= mid){
		rson[u] = rson[v];
		UPDATE(lson[u], lson[v], l, mid, x);
	}
	else{
		lson[u] = lson[v];
		UPDATE(rson[u], rson[v], mid+1, r, x);
	}
}

int QUERY(int u, int v, int l, int r, int k)
{
	if (l == r) return l;
	int cnt = sum[lson[u]]-sum[lson[v]];
	int mid = (l+r)>>1;
	if (k <= cnt)
		return QUERY(lson[u], lson[v], l, mid, k);
	else
		return QUERY(rson[u], rson[v], mid+1, r, k-cnt);
}

int main()
{
	scanf("%d%d", &n, &q);
	for (int i = 1; i <= n; i++){
		scanf("%d", &a[i]);
		v.push_back(a[i]);
	}
	sort(v.begin(), v.end());
	v.erase(unique(v.begin(), v.end()), v.end());
	sz = v.size(); tot = 0;
	BUILD(root[0], 0, sz-1);
	for (int i = 1; i <= n; i++)
		UPDATE(root[i], root[i-1], 0, sz-1, GETID(a[i]));
	for (int i = 1; i <= q; i++){
		int ql, qr, k;
		scanf("%d%d%d", &ql, &qr, &k);
		printf("%d\n", v[QUERY(root[qr], root[ql-1], 0, sz-1, k)]);
	}
	return 0;
}


二、动态主席树

猜你喜欢

转载自blog.csdn.net/xyyxyyx/article/details/81021521