ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

主席树

2022-04-15 09:00:25  阅读:173  来源: 互联网

标签:rt ch int sum ls lca 主席


模板题:静态区间的第k小

主席树:可持久化线段树,可以支持查询历史版本的线段树。

在模板题中,对数组的每一个前缀建立一棵权值线段树,线段树的每一个节点维护属于区间\([l,r]\)的数的个数。

对于询问\([l_i,r_i]\)中的第\(k_i\)大的数,利用前缀的思想,版本\(r_i\)与\(l_{i-1}\)之间的差就是\([l_i,r_i]\)的信息。左儿子中数的个数为\(sum_l\),如果\(sum_l \ge k_i\),继续查询左儿子中第\(k_i\)大的数,如果\(sum_l<k_i\),查询右儿子中第\(k_i-sum_l\)大的数。

如果对于每个版本都建立一棵线段树,会MLE。但两个相邻版本的线段树,对一个节点,只会改变一个儿子,另一个儿子没有改变。没有改变的儿子用上一个版本的儿子,发生的儿子新建一个节点,所以要动态开点。

权值线段树维护的是\([l,r]\)中数的个数,所以要排序之后离散化,重要的是大小,不是数值。查询的结果是第\(k\)大的数在原数组是第几大的

查找和更新的时间复杂度为\(O(logn)\).
空树的空间复杂度为\(O(nlogn)\),更新的空间复杂度为\(O(logn)\) ,所以内存池至少开\(32n\)

代码:

点击查看代码
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 2E5+5;
typedef long long ll;
inline int read(){//读入优化 
	char ch; int flag = 1;
	while((ch=getchar())<'0'||ch>'9')
		if(ch == '-') flag = -1;
	int res = ch-48;
	while((ch=getchar())>='0'&&ch<='9')
		res = res*10+ch-48;
	return res*flag;
}
int n, q, m, cnt = 0;
int a[maxn], b[maxn], root[maxn];
int sum[maxn<<5], ls[maxn<<5], rs[maxn<<5];
inline int build(int l, int r){
	int rt = ++cnt;
	sum[rt] = 0;
	if(l == r)
		return rt;
	int mid = (l+r) >> 1; 
	ls[rt] = build(l, mid);
	rs[rt] = build(mid+1, r);
	return rt;
}
inline int update(int pre, int l, int r, int x){
	int rt = ++cnt;
	ls[rt] = ls[pre], rs[rt] = rs[pre], sum[rt] = sum[pre]+1;
	if(l == r)
		return rt;
	int mid = (l+r) >> 1;
	if(x <= mid)
		ls[rt] = update(ls[pre], l, mid, x);
	else 
		rs[rt] = update(rs[pre], mid+1, r, x);
	return rt;
}

inline int query(int x, int y, int l, int r, int k){
	if(l == r)
		return l;
	int mid = (l+r) >> 1;
	int tmp = sum[ls[y]] - sum[ls[x]];
	if(tmp >= k)
		return query(ls[x], ls[y], l, mid, k);
	else
		return query(rs[x], rs[y], mid+1, r, k-tmp);
	
}
int main(){
	n = read(), q = read();
	for(int i = 1; i <= n; i++)
		a[i] = b[i] = read();
	sort(b+1, b+1+n);
	m = unique(b+1, b+1+n)-(b+1);
	for(int i = 1; i <= n; i++){
		int s = lower_bound(b+1, b+1+m, a[i])-b;
		root[i] = update(root[i-1], 1, m, s);
	}
	int x, y, k;
	for(int i = 1; i <= q; i++){
		x = read(), y = read(), k = read();
		int t = query(root[x-1], root[y], 1, m, k);
		cout << b[t] << endl;
	}
	return 0;
}




模板题:树的路径上第k小(强制在线)

主席树维护的是前缀区间信息。设根节点是root,版本rt[i]维护的是root~i路径上的题。则u到v路径上的信息在\(rt[u]+rt[v]-rt[lca]-rt[fa[lca]]\)中,然后在树上查询第\(k\)小.

在树剖dfs的时候顺便建立线段树,数据离散化

代码:

点击查看代码
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn = 2e5+10;
int fa[maxn], siz[maxn], top[maxn], son[maxn], dep[maxn];
struct edge{
	int to, nxt;
}e[maxn<<1];
int head[maxn], tot;
int sum[maxn<<5], ls[maxn<<5], rs[maxn<<5], root[maxn];
int a[maxn], b[maxn];
int n, m, q, cnt, ans;
void add(int u, int v){
	e[++tot].nxt = head[u];
	e[tot].to = v;
	head[u] = tot;
}
int update(int pre, int l, int r, int x){
	int rt = ++cnt;
	sum[rt] = sum[pre] + 1;
	if(l == r)
		return rt;
	int mid = (l+r) >> 1;
	if(x <= mid){
		ls[rt] = update(ls[pre], l, mid, x);
		rs[rt] = rs[pre];
	}
	
	else{
		rs[rt] = update(rs[pre], mid+1, r, x);
		ls[rt] = ls[pre];
	}
		
	return rt;
}
void dfs1(int u){
	siz[u] = 1;
	dep[u] = dep[fa[u]] + 1;
	root[u] = update(root[fa[u]], 1, m, a[u]);
	for(int i = head[u]; i; i = e[i].nxt){
		int v = e[i].to;
		if(v == fa[u])
			continue;
		fa[v] = u;
		dfs1(v);
		siz[u] += siz[v];
		if(!son[u] || siz[v] > siz[son[u]])
			son[u] = v;
	}
}
void dfs2(int u, int tp){
	top[u] = tp;
	if(!son[u])
		return;
	dfs2(son[u], tp);
	for(int i = head[u]; i; i = e[i].nxt){
		int v = e[i].to;
		if(v == fa[u] || v == son[u])
			continue;
		dfs2(v, v);
	}
}
int LCA(int x, int y){
	while(top[x] != top[y]){
		if(dep[top[x]] >= dep[top[y]])
			x = fa[top[x]];
		else
			y = fa[top[y]];
	}
	return dep[x] >= dep[y] ? y : x;
}
int query(int ql, int qr, int lca, int lca_fa, int l ,int r, int k){
	if(l == r)
		return l;
	int x = sum[ls[ql]] + sum[ls[qr]] - sum[ls[lca]] - sum[ls[lca_fa]];
	int mid = (l+r) >> 1;
	if(x >= k)
		return query(ls[ql], ls[qr], ls[lca], ls[lca_fa], l, mid, k);
	else
		return query(rs[ql], rs[qr], rs[lca], rs[lca_fa], mid+1, r, k-x);
}
inline ll read(){//读入优化 
	char ch;
	ll sign = 1;
	while((ch=getchar())<'0'||ch>'9')
		if(ch == '-')
			sign = -1;
	ll res = ch-48;
	while((ch=getchar())>='0'&&ch<='9')
		res = res*10+ch-48;
	return res*sign;
}
int main(){
	n = read(), q = read();
	for(int i = 1; i <= n; i++){
		a[i] = b[i] = read();
	}
	sort(b+1, b+1+n);
	m = unique(b+1, b+1+n) - (b+1);
	for(int i = 1; i <= n; i++)
		a[i] = lower_bound(b+1, b+1+m, a[i]) - b;
	for(int i = 1; i < n; i++){
		int u, v;
		u = read(), v = read();
		add(u, v);
		add(v, u);
	}
	dfs1(1);
	dfs2(1, 1);
	while(q--){
		int x, y, k, lca;
		x = read(), y = read(), k = read();
		x ^= ans;
		lca = LCA(x, y);
		ans=b[query(root[x],root[y],root[lca],root[fa[lca]],1,m,k)];
		cout << ans << endl;
	}
	return 0;
}

标签:rt,ch,int,sum,ls,lca,主席
来源: https://www.cnblogs.com/Ruthenium/p/16142779.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有