P3834 【模板】可持久化线段树 1(主席树)
题目背景
这是个非常经典的主席树入门题——静态区间第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
说明
数据范围:
对于20%的数据满足:1 leq N, M leq 101≤N,M≤10
对于50%的数据满足:1 leq N, M leq 10^31≤N,M≤103
对于80%的数据满足:1 leq N, M leq 10^51≤N,M≤105
对于100%的数据满足:1 leq N, M leq 2cdot 10^51≤N,M≤2⋅105
对于数列中的所有数a_iai,均满足-{10}^9 leq a_i leq {10}^9−109≤ai≤109
样例数据说明:
N=5,数列长度为5,数列从第一项开始依次为[25957, 6405, 15770, 26287, 26465 ][25957,6405,15770,26287,26465]
第一次查询为[2, 2][2,2]区间内的第一小值,即为6405
第二次查询为[3, 4][3,4]区间内的第一小值,即为15770
第三次查询为[4, 5][4,5]区间内的第一小值,即为26287
第四次查询为[1, 2][1,2]区间内的第二小值,即为25957
第五次查询为[4, 4][4,4]区间内的第一小值,即为26287
主席树真是个好东西,比划分树不知高到哪里去.
主席树
#include<stdio.h> #include<algorithm> #define FOR(i,s,t) for(register int i=s;i<=t;++i) using std::sort; using std::lower_bound; using std::unique; const int NlogN=3521999; const int N=200011; int a[N],b[N]; int n,m,len,tot; int l,r,k; int rt[N]; inline void disc_init(){ sort(b+1,b+n+1); len=unique(b+1,b+n+1)-b-1; FOR(i,1,n)a[i]=lower_bound(b+1,b+len+1,a[i])-b; } struct Tree{ int ls,rs,sum; }tr[NlogN<<2]; inline void insert(int &x,int &y,int l,int r,int p){ tr[x=++tot]=tr[y]; ++tr[x].sum; if(l==r)return; int mid=(l+r)>>1; p<=mid?insert(tr[x].ls,tr[y].ls,l,mid,p):insert(tr[x].rs,tr[y].rs,mid+1,r,p); } inline void build_tree(){ FOR(i,1,n)insert(rt[i],rt[i-1],1,len,a[i]); } inline int query(int x,int y,int l,int r,int p){ if(l==r)return l; int mid=(l+r)>>1; int data=tr[tr[x].ls].sum-tr[tr[y].ls].sum; return p<=data?query(tr[x].ls,tr[y].ls,l,mid,p):query(tr[x].rs,tr[y].rs,mid+1,r,p-data); } int main(){ scanf("%d%d",&n,&m); FOR(i,1,n)scanf("%d",a+i),b[i]=a[i]; disc_init(); build_tree(); while(m--){ scanf("%d%d%d",&l,&r,&k); printf("%d ",b[query(rt[r],rt[l-1],1,len,k)]); } return 0; }
划分树
#include<stdio.h> #include<algorithm> #define FOR(i,s,t) for(register int i=s;i<=t;++i) using std::sort; const int N=1000011; typedef long long LL; int a[N]; int sorted[N]; int num[21][N]; int val[21][N]; inline void build(int l,int r,int dep){ if(l==r) return ; int mid=(l+r)>>1,isame=mid-l+1; FOR(i,l,r) if(val[dep][i]<sorted[mid]) --isame; int ln=l,rn=mid+1; FOR(i,l,r){ i==l?num[dep][i]=0:num[dep][i]=num[dep][i-1]; if(val[dep][i]<sorted[mid]||(val[dep][i]==sorted[mid]&&isame>0)){ val[dep+1][ln++]=val[dep][i]; ++num[dep][i]; if(val[dep][i]==sorted[mid]) --isame; } else val[dep+1][rn++]=val[dep][i]; } build(l,mid,dep+1); build(mid+1,r,dep+1); } inline int query(int dep,int sl,int sr,int l,int r,int k){ if(sl==sr) return val[dep][sl]; int ly; l==sl?ly=0:ly=num[dep][l-1]; int tolef=num[dep][r]-ly; int mid=(sl+sr)>>1; int lr=mid+1+(l-sl-ly); return tolef>=k?query(dep+1,sl,mid,sl+ly,sl+num[dep][r]-1,k):query(dep+1,mid+1,sr,lr,lr+r-l-tolef,k-tolef); } int main(){ int n,m,l,r,k; scanf("%d%d",&n,&m); FOR(i,1,n){ scanf("%d",&val[0][i]); sorted[i]=val[0][i]; } sort(sorted+1,sorted+n+1); build(1,n,0); while(m--){ scanf("%d%d%d",&l,&r,&k); printf("%d ",query(0,1,n,l,r,k)); } return 0; }