题目背景
这是个非常经典的主席树入门题——静态区间第K小
数据已经过加强,请使用主席树。同时请注意常数优化
题目描述
如题,给定N个整数构成的序列,将对于指定的闭区间查询其区间内的第K小值。
输入格式
第一行包含两个正整数N、M,分别表示序列的长度和查询的个数。
第二行包含N个整数,表示这个序列各项的数字。
接下来M行每行包含三个整数l,r,k , 表示查询区间l,r]内的第k小值。
输出格式
输出包含k行,每行1个整数,依次表示每一次查询的结果
输入输出样例
5 5 25957 6405 15770 26287 26465 2 2 1 3 4 1 4 5 1 1 2 2 4 4 1
6405 15770 26287 25957 26287
说明/提示
数据范围:
对于20%的数据满足:1≤N,M≤10
对于50%的数据满足:1≤N,M≤10^3
对于80%的数据满足:1≤N,M≤10^5
对于100%的数据满足:1≤N,M≤2⋅10^5
对于数列中的所有数a_iai,均满足−10^9≤ai≤10^9
样例数据说明:
N=5,数列长度为5,数列从第一项开始依次为[25957,6405,15770,26287,26465]
第一次查询为[2,2]区间内的第一小值,即为6405
第二次查询为[3,4]区间内的第一小值,即为15770
第三次查询为[4,5]区间内的第一小值,即为26287
第四次查询为[1,2]区间内的第二小值,即为25957
第五次查询为[4,4]区间内的第一小值,即为26287
这是一题很经典的静态主席树,对于主席树而言,其实它就是可持续化线段树与权值线段树的结合体而已。
其核心就是update的可持续化过程与query的权值线段树询问过程:
int update(int l,int r,int pos,int per) { int rt=++tot; int mid=(l+r)>>1; lson[rt]=lson[per];rson[rt]=rson[per];sum[rt]=sum[per]+1; if (l==r) { //sum[rt]=sum[per]+1; return rt; } if (mid>=pos) lson[rt]=update(l,mid,pos,lson[per]); else rson[rt]=update(mid+1,r,pos,rson[per]); //sum[rt]=sum[lson[rt]]+sum[rson[rt]]; return rt; }
该update程序段解释入下:
这个update过程应该挺清楚的,传入的参数是当前节点的l,r和要更新的位置以及上一个版本的当前位置的节点编号。那么接下来遍历所经过的所有的节点都是会因为pos的改变而改变:
如上图所示,第一次修改时只会经过红色部分,那么也就是说,没有经过的点保持原来的节点编号,经过的节点另开空间(和动态开点一样)同时编号增加。
我们可以先将当前节点的左右儿子编号赋值为上一个版本的当前节点的左右儿子编号,然后要修改的时候直接覆盖就好了。
同时,这里更新sum有两种写法,1——由于他每次之会更新一个节点,所以我们可以直接在他经过的路上+1就好了:sum[rt]=sum[per]+1;上一个版本当前节点的值+1
2——和线段树一样,我们在他更新到叶子节点的时候再更新,最后加一个push_up就好了
int query(int l,int r,int L,int R,int k) { if (l==r) return l; int mid=(l+r)>>1; int num=sum[lson[R]]-sum[lson[L]]; if (num>=k) return query(l,mid,lson[L],lson[R],k); else return query(mid+1,r,rson[L],rson[R],k-num); }
该query程序段解释如下:
我们可以构想一下,先在左子树中找,如果左子树的sum大于k也就是说,该区间存在第k小的数在左子树中,那么我们就在左子树中找,否则就存在右子树中,我们在右子树中找,同时传入k-左子树的sum。那么代码也就出来了。
以下是AC代码:
#include <bits/stdc++.h> using namespace std; const int mac=2e5+10; int a[mac],sum[mac<<6],lson[mac<<6],rson[mac<<6]; int tot=0,b[mac],ver[mac]; int build(int l,int r) { int rt=++tot; if (l==r) return rt; int mid=(l+r)>>1; lson[rt]=build(l,mid); rson[rt]=build(mid+1,r); return rt; } int update(int l,int r,int pos,int per) { int rt=++tot; int mid=(l+r)>>1; lson[rt]=lson[per];rson[rt]=rson[per];sum[rt]=sum[per]+1; if (l==r) { //sum[rt]=sum[per]+1; return rt; } if (mid>=pos) lson[rt]=update(l,mid,pos,lson[per]); else rson[rt]=update(mid+1,r,pos,rson[per]); //sum[rt]=sum[lson[rt]]+sum[rson[rt]]; return rt; } int query(int l,int r,int L,int R,int k) { if (l==r) return l; int mid=(l+r)>>1; int num=sum[lson[R]]-sum[lson[L]]; if (num>=k) return query(l,mid,lson[L],lson[R],k); else return query(mid+1,r,rson[L],rson[R],k-num); } void in(int &x) { int f=0,mark=1; char ch=getchar(); while ((ch>'9' || ch<'0') && ch!='-') ch=getchar(); if (ch=='-') mark=-1; while (ch>='0' && ch<='9') f=(f<<1)+(f<<3)+ch-'0',ch=getchar(); x=f*mark; } int main() { //freopen("in.txt","r",stdin); int n,m; in(n);in(m); for (int i=1; i<=n; i++) in(a[i]),b[i]=a[i]; sort(b+1,b+1+n); int p=unique(b+1,b+1+n)-b-1; ver[0]=build(1,p); for (int i=1; i<=n; i++){ int x=lower_bound(b+1,b+1+p,a[i])-b; ver[i]=update(1,p,x,ver[i-1]); } while (m--){ int l,r,k; in(l);in(r);in(k); int ans=query(1,p,ver[l-1],ver[r],k); printf ("%d ",b[ans]); } return 0; }