[知识点]主席树入门 区间k值

入坑主席树主要是因为昨天考试的后两道题不可改2333 而且觉得这个还挺有用,于是果断入坑

不过蒟蒻的我只是在上午看了看大概思路,下午开的运动会没时间码,于是晚上码了出来。但是目前只会无修改的区间K值问题,加上又比较抽象,思路屡了半天才刚刚醒悟,于是写下来记录一下。

不扯那么多辣鸡套路,直接说思路打法(以k小值为例):

我们先要以权值建一颗线段树,然后每个节点记录的是这个节点管辖的范围内数列中数的个数。

我们需要建好多好多线段树。简单来讲呢,就是[1,1],[1,2]···[1,n]这么多颗,每棵树都是[1,i]按权值建树。

我们要求区间[L,R]中第k小的数,假设我们知道[1,L-1]之间有多少个数比第k小的数小,那么我们只要减去这些数之后在[1, R]区间内第k小的数即是[l, R]区间内的第k小数。

更确切的说,我们要求[L,R]区间内的第k小数  可以 用以[1, R]建立的线段树去减去以[1, L-1] 建立的线段树。(两棵树必须是同构的)

好了现在我们搞到了区间[L,R]这一段对应的线段树,那么我们怎么找k值呢?

若左子树大于k,那么很显然第k小的数在左子树中;若左子树小于k,那么第k小的数在右子树中。

我们来举一个例子:序列  1 2 5 1 3 2 2 5 1 2

我们要求 [5,10]第5小的数

(红色的为个数)

我们建立的[1, l-1] (也就是[1, 4])之间的树为

[1, r]也就是[1, 10]的树为

两树相减得到

我们来找第5小的数:

发现左子树为5  所以第5小的数在左边, 再往下(左4右1) 发现左边小于5了 ,所以第5小的数在右边 所以第5小的数就是3了

同样的,我们只要建立[1, i] (i是1到n之间的所有值)的所有树,每当询问[l, r]的时候,只要用[1, r]的树减去[1, l-1]的树,再找第k小就好啦

但是,有一个问题发现没有?开这么多树是要炸啊??

我们发现,[1,i]和[1,i+1]所建的线段树只是加了一个a[i]罢了,它们改变的只有a[i]那一条链,其他的是可以共用的!

所以就可以这么搞了。但是还是有一个问题:我们按权值建树的话也可能会炸的,所以我们需要离散

我们每次抻出来一个新根节点,加入一下a[i]离散后的值。

我们注意上一个例子,我们找到符合要求的之后,可以直接返回下标3,因为我们是按权值建的树。但是这次我们是按离散之后建的树

所以我们返回的下标 i 是在离散之后排名为 i 的数,我们要返回它原来的值。

不够这里需要注意一下:如果你离散去重了,那么最后返回排名为 i 的数也一定是去重之后的排名为 i 的数

上例题  cogs930  [河南省队2012] 找第k小的数

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define pos(i,a,b) for(int i=(a);i<=(b);i++)
#define N 101000
#include<vector>
#include<algorithm>
int n,m;
int a[N];
vector<int> b;
int find(int x){
	return lower_bound(b.begin(),b.end(),x)-b.begin()+1;
}
struct haha{
	int lc,rc,num;
}tree[N*20];
int root[N],t;
void build(int num,int &x,int l,int r){
	tree[++t]=tree[x];
	x=t;
	++tree[x].num;
	if(l==r) return;
	int mid=(l+r)>>1;
	if(num<=mid) build(num,tree[x].lc,l,mid);
	else build(num,tree[x].rc,mid+1,r);
}
int query(int i,int j,int k,int l,int r){
	if(l==r) return l;
	int cnt=tree[tree[j].lc].num-tree[tree[i].lc].num;
	int mid=(l+r)>>1;
	if(k<=cnt) return query(tree[i].lc,tree[j].lc,k,l,mid);
	else return query(tree[i].rc,tree[j].rc,k-cnt,mid+1,r);
}
int main(){
	//freopen("kthnumber.in","r",stdin);
    //freopen("kthnumber.out","w",stdout);
	scanf("%d%d",&n,&m);
	pos(i,1,n){
		scanf("%d",&a[i]);
		b.push_back(a[i]);
	}
	sort(b.begin(),b.end());
	b.erase(unique(b.begin(),b.end()),b.end());
	tree[0].lc=tree[0].rc=tree[0].num=0;
	pos(i,1,n){
		root[i]=root[i-1];
		build(find(a[i]),root[i],1,n);
	}
	sort(a+1,a+n+1);
	unique(a+1,a+n+1);
	pos(i,1,m){
		int l,r,k;scanf("%d%d%d",&l,&r,&k);
		printf("%d\n",a[query(root[l-1],root[r],k,1,n)]);
	}
	return 0;
}

  

时间: 2024-11-05 16:39:14

[知识点]主席树入门 区间k值的相关文章

Super Mario(线段树离线区间k值)

以前见过这题,没做出来,知道是离线处理,这次仔细想了下, 首先把出现的高度都map离散化一下,以离散化出来的数目g建树,把每个位置都开俩个vector,一个存以这个位置为L的询问,一个存以这个位置为R的询问. 然后从1-g 进行更新,假如当前i是以第j个区间的开始位置,那么这时就可以询问一下<=p[j].h的个数s,显然这时第J个区间多加的,需要减掉,p[j].sum-=s; 然后更新第i个数,update(a[i],1,g,1);再找到某第k区间是以i结尾的,那么依旧询问一下,得出s,p[k]

POJ 2104:K-th Number(主席树静态区间k大)

题目大意:对于一个序列,每次询问区间[l,r]的第k大树. 分析: 主席树模板题 program kthtree; type point=record l,r,s:longint; end; var t:array[0..100000*50]of point; a,b,id,root:array[0..100000]of longint; n,i,m,x,y,k,v,len:longint; procedure qsort(l,h:longint); var i,j,t,m:longint; b

HDU 5919 - Sequence II (2016CCPC长春) 主席树 (区间第K小+区间不同值个数)

HDU 5919 题意: 动态处理一个序列的区间问题,对于一个给定序列,每次输入区间的左端点和右端点,输出这个区间中:每个数字第一次出现的位子留下, 输出这些位子中最中间的那个,就是(len+1)/2那个. 思路: 主席树操作,这里的思路是从n到1开始建树.其他就是主席树查询区间第K小,计算区间不同值个数. #include <algorithm> #include <iterator> #include <iostream> #include <cstring&

[poj 2104]主席树+静态区间第k大

题目链接:http://poj.org/problem?id=2104 主席树入门题目,主席树其实就是可持久化权值线段树,rt[i]维护了前i个数中第i大(小)的数出现次数的信息,通过查询两棵树的差即可得到第k大(小)元素. #include<cstdio> #include<vector> #include<algorithm> using namespace std; #define lson(i) node[i].lson #define rson(i) node

【BZOJ2653】middle,主席树(非权值线段树)维护区间01信息+二分答案

传送门 写在前面:虽然这是一道我再也不想写的题目,但很好很有价值 思路: cxlove大神: 要求中位数最大,首先二分中位数,然后判断可行不可行. 判断X可行不可行,对于区间内的数,凡是>=X的标为1,否则为-1.这样的话,求一次最大区间和 如果大于等于0,则说明可行. 这要求我们不能像之前那样建立权值线段树的主席树(区间即为权值)了,而是以权值为下标,维护区间[1,n]的信息,可能有点拗口,这里就理解是我们平常写的普通线段树好了,只是这里是n棵由于根的不同而信息不同的线段树 具体实现 对于题目

Poj 2104(主席树入门

题目:静态查询区间第k大. 主席树入门题目,之前看的很多资料一上来就是动态区间第k大,看得很费劲,后来找了个写得清晰的,感觉静态的还不算难,代码也不长. /* * @author: Cwind */ //#pragma comment(linker, "/STACK:102400000,102400000") #include <iostream> #include <map> #include <algorithm> #include <cs

zoj 2112 Dynamic Rankings(主席树&amp;动态第k大)

Dynamic Rankings Time Limit: 10 Seconds      Memory Limit: 32768 KB The Company Dynamic Rankings has developed a new kind of computer that is no longer satisfied with the query like to simply find the k-th smallest number of the given N numbers. They

LightOJ Array Queries 1082【线段树求区间最值】

1082 - Array Queries PDF (English) Statistics Forum Time Limit: 3 second(s) Memory Limit: 64 MB Given an array with N elements, indexed from 1 to N. Now you will be given some queries in the form I J, your task is to find the minimum value from index

【题解】CODEVS 1217 借教室 zkw线段树(区间最值)

捣鼓了好长时间才终于把zkw线段树的区间最值修改给A掉了 = =zkw神犇的课件里竟然有错! (╯▽╰)下面的伪代码里被注释的是我修改的>< Func Add_x(s,t,x) for (s=s+M-1,t=t+M+1;s^t^1;s>>=1,t>>=1) { if (~s&1) T[s^1]+=x; if ( t&1) T[t^1]+=x; A=min(T[s],T[s^1]),T[s]-=A,T[s^1]-=A, T[s>>1]+=A;