数据结构之主席树

这里先讲静态的主席树,关于静态区间第k小。(有兴趣的朋友还可以去看看我写的整体二分,代码实现略优于主席树我觉得,当然静态主席树是很好写的)

题目描述:

题目描述

如题,给定N个正整数构成的序列,将对于指定的闭区间查询其区间内的第K小值。

输入输出格式

输入格式:

第一行包含两个正整数N、M,分别表示序列的长度和查询的个数。

第二行包含N个正整数,表示这个序列各项的数字。

接下来M行每行包含三个整数 l, r, kl,r,k , 表示查询区间 [l, r][l,r] 内的第k小值。

输出格式:

输出包含k行,每行1个正整数,依次表示每一次查询的结果

输入输出样例

输入样例#1:

5 5
25957 6405 15770 26287 26465
2 2 1
3 4 1
4 5 1
1 2 2
4 4 1

输出样例#1:

6405
15770
26287
25957
26287

那么我们明确主席树是个什么东西。它就是,可持久化线段树。首先我们不考虑主席树,而是对于这个区间第k小问题做一个分析。如果我们对每一个区间暴力快排,毋庸置疑,绝对炸上天,T到你想哭。于是聪明的前人发现了这个问题的一个特点,下面我来说说。先把每一个数值离散化,树中每一个值就即是权值也是排名了(离散化建议看看我之前写的离散化博客里的代码)。好吧我到现在还没有说明这个树到底是个什么玩意儿。

发明者的原话:“对于原序列的每一个前缀[1···i]建立出一棵线段树维护值域上每个数出现的次数,则其树是可减的”

可以加减的理由:主席树的每个节点保存的是一颗线段树,维护的区间信息,结构相同,因此具有可加减性(关键)

是的,我们对于每一个前缀建一颗树,每一段区间维护的是当前区间的点的个数,注意这里的区间不是位置的区间,而是权值的区间,

这就是为什么要离散化了。

那么减可以干什么呢?注意这是个很重要的思想,前缀和。

先考虑一个问题,如果每次询问的l都是数组一开头的位置1,是不是很容易维护?

下面举一个例子:维护数组6 2 3 1 4 5。 对前缀1~6建树 (图难看。。。不打紧的咳咳),这个求第k小应该一目了然吧

然而,区间第k小其实很容易,对于区间[l,r]而言,只需要在每个节点用前缀r的节点值减去前缀l-1的节点值就好了,其他的和上面是一样的

下面附上一组大佬的图

注意和我的数据是不一样的,他的是4 1 1 2 8 9 4 4 3

然后读者按照我刚刚说的用前缀r的树的每一个节点减去对应的前缀l-1的树的节点得到一颗新的树就好

好的,现在我们回到原来的问题,主席树。其实上面就是主席树,但是若是我们对于每一个前缀都暴力建一次树的话,在时间和空间上都不能接受。

这时候我们注意到相邻的两棵树其实是很相似的,我们可以让这一棵树和上一棵树共用一些节点,从而达到减低空间和时间复杂度的效果。前缀每次向右一位,其实就是多插入了一个数值,新的

树和上一棵树的唯一区别其实就是一条链上变了而已。

事实证明,这种方法十分的有效。

就像这样,在原来的基础上加上几个点

下面我附上我的代码供大家研究,其实我认为代码更加好懂

#include<bits/stdc++.h>
using namespace std;

const int maxn=2e5+15;
int n,m,cnt;
int a[maxn],b[maxn],tree[maxn<<5],L[maxn<<5],R[maxn<<5],sum[maxn<<5];
int build(int l,int r)
{
	int rt=++cnt;
	sum[rt]=0;
	if (l<r)
	{
		int mid=(l+r)>>1;
		L[rt]=build(l,mid);
		R[rt]=build(mid+1,r);
	}
	return rt;
}
int update(int pre,int l,int r,int x)
{
	int rt=++cnt;
	L[rt]=L[pre];R[rt]=R[pre];sum[rt]=sum[pre]+1;//多插入了点于是加个1
	if (l<r)
	{
		int mid=(l+r)>>1;
		if (x<=mid) L[rt]=update(L[pre],l,mid,x);//看看插到哪一边,另一边其实是一样的
		else R[rt]=update(R[pre],mid+1,r,x);
	}
	return rt;
}
int query(int u,int v,int l,int r,int k)
{
	if (l>=r) return l;
	int x=sum[L[v]]-sum[L[u]];//减一下就好
	int mid=(l+r)>>1;
	if (x>=k) return query(L[u],L[v],l,mid,k);
	else return query(R[u],R[v],mid+1,r,k-x);
}
int main()
{
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		b[i]=a[i];
	}
	sort(b+1,b+1+n);//离散化
	int q=unique(b+1,b+1+n)-b-1;
	tree[0]=build(1,q);
	for (int i=1;i<=n;i++)
	{
		int t=lower_bound(b+1,b+1+q,a[i])-b;
		tree[i]=update(tree[i-1],1,q,t);//通过上一个树建树
	}
	while (m--)
	{
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		int t=query(tree[x-1],tree[y],1,q,z);//通过x-1树和y树相减
		printf("%d\n",b[t]);
	}
	return 0;
}

  上文部分图来自大佬 Lpy_Now,感谢大佬

原文地址:https://www.cnblogs.com/xxzh/p/9158819.html

时间: 2024-09-28 15:57:27

数据结构之主席树的相关文章

可持久化数据结构之主席树

转自:http://finaltheory.info/?p=249 HomeACM可持久化数据结构之主席树 06十2013 可持久化数据结构之主席树 Written by FinalTheory on. Posted in ACM 引言 首先引入CLJ论文中的定义: 所谓的“持久化数据结构”,就是保存这个数据结构的所有历史版本,同时利用它们之间的共用数据减少时间和空间的消耗. 本文主要讨论两种可持久化线段树的算法思想.具体实现以及编码技巧. 核心思想 可持久化线段树是利用函数式编程的思想,对记录

数据结构(主席树):HZOI 2016 采花

[题目描述] 给定一个长度为n,包含c种颜色的序列,有m个询问,每次给出两个数l,r,表示询问区间[l,r]中有多少种颜色的出现次数不少于2次. 本题强制在线,对输入的l,r进行了加密,解密方法为: l = l' xor lastans r = r' xor lastans 其中l', r'为输入的l和r,xor表示异或,lastans为上一次询问的答案且初始值为0. [输入格式] 第一行三个正整数n,c,m,意义与题目描述中的相同. 第二行n个位于[1,c]内的正整数,表示序列上每个位置的颜色

【重码数据结构】主席树(可持久化线段树)

例题:https://www.luogu.org/problemnew/show/P3834 主席树用于查询每个历史版本. 这个题代码如下 #include<bits/stdc++.h> #define ll long long using namespace std; const int maxn=200001; int n,m,tot; ll a[maxn],b[maxn]; struct stree{ int lc,rc; ll sum; #define lc(x) tree[x].lc

数据结构(主席树,Bit):XTU 1247/COGS 2344. pair-pair

pair-pair 输入文件:pair-pair.in   输出文件:pair-pair.out   简单对比 时间限制:7 s   内存限制:64 MB Time Limit : 7000 MS Memory Limit : 65536 KB Pair-Pair Bobo is tired of all kinds of hard LIS (Longest Increasing Subsequence) problems, so he decides to make himself some

数据结构(主席树):HDU 4729 An Easy Problem for Elfness

An Easy Problem for Elfness Time Limit: 5000/2500 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others)Total Submission(s): 1148    Accepted Submission(s): 234 Problem Description Pfctgeorge is totally a tall rich and handsome guy. He plans t

【bzoj3744】Gty的妹子序列 分块+树状数组+主席树

题目描述 我早已习惯你不在身边, 人间四月天 寂寞断了弦. 回望身后蓝天, 跟再见说再见…… 某天,蒟蒻Autumn发现了从 Gty的妹子树(bzoj3720) 上掉落下来了许多妹子,他发现 她们排成了一个序列,每个妹子有一个美丽度. Bakser神犇与他打算研究一下这个妹子序列,于是Bakser神犇问道:"你知道区间 [l,r]中妹子们美丽度的逆序对数吗?" 蒟蒻Autumn只会离线乱搞啊……但是Bakser神犇说道:"强制在线." 请你帮助一下Autumn吧.

[可持久化线段树(主席树)]

主席树 抛出问题 如题,给定N个整数构成的序列,将对于指定的闭区间查询其区间内的第K小值. 输入输出格式 输入格式: 第一行包含两个正整数N.M,分别表示序列的长度和查询的个数. 第二行包含N个整数,表示这个序列各项的数字. 接下来M行每行包含三个整数l, r, kl,r,k , 表示查询区间[l, r][l,r]内的第k小值. 输出格式: 输出包含k行,每行1个整数,依次表示每一次查询的结果 解决问题 主席树(可持久化线段树)法 于是针对这个问题,新的数据结构诞生了,也就是主席树. 主席树本名

基础主席树

我觉得数据结构比其他东西有趣多了,所以我现在沉迷数据结构... 正题: 主席树 又名可持久化线段树,(其实应该反过来,最后说说这个问题[doge]) 建议先掌握线段树 所谓可持久化,顾名思义,就是"持久",也就是运行时间长, 非也,是支持关于历史版本的操作, 举个栗子: 现在给定数列\(a\),以及若干次单元素修改, 每次修改会产生一个版本,可以理解为每次修改会产生一个新的数列, 每次修改还基于另一个版本,即一次修改可能在另一次修改之上 (其实也可能在原序列之上,其实就是对版本&quo

【模板】【数据结构】【树】主席树

技巧一:离散去重 for(int i=1;i<=n;i++) scanf("%d",&a[i] ),b[i]=a[i]; sort(b+1,b+n+1); int nn=unique(b+1,b+n+1)-b-1;//假设有x个数,那么nn指针会停在第x+1个数的位置 ,nn及以后的都是重复的元素for(int i=1;i<=n;i++) id[i]=lower_bound(b+1,b+nn+1,a[i])-b;//离散过后的新value 技巧二:可持久化数据结构