Time Limit: 20000MS | Memory Limit: 65536K | |
Total Submissions: 46358 | Accepted: 15446 | |
Case Time Limit: 2000MS |
Description
Input
Output
Sample Input
7 3 1 5 2 6 3 7 4 2 5 3 4 4 1 1 7 3
Sample Output
5 6 3
Hint
Source
题意:求查询区间第K大的数字
主席树的入门题。懵比中,感觉和线段树好像,可是有很不一样,把别人的代码抄了一边
主席树的主体是线段树,准确的说,是很多棵线段树,存的是一段数字区间出现次数(所以要先离散化可能出现的数字)。举个例子,假设我每次都要求整个序列内的第 k 小,那么对整个序列构造一个线段树,然后在线段树上不断找第 k 小在当前数字区间的左半部分还是右半部分。这个操作和平衡树的 Rank 操作一样,只是这里将离散的数字搞成了连续的数字。
先假设没有修改操作:
对于每个前缀 S1…i,保存这样一个线段树 Ti,组成主席树。这样不是会 MLE 么?最后再讲。
注意,这个线段树对一条线段,保存的是这个数字区间的出现次数,所以是可以互相加减的!还有,由于每棵线段树都要保存同样的数字,所以它们的大小、形态也都是一样的!这实在是两个非常好的性质,是平衡树所不具备的。
对于询问 (i,j),我只要拿出 Tj 和 Ti-1,对每个节点相减就可以了。说的通俗一点,询问 i..j 区间中,一个数字区间的出现次数时,就是这些数字在 Tj 中出现的次数减去在 Ti-1 中出现的次数。
那么有修改操作怎么办呢?
如果将询问看成求一段序列的数字和,那么上面那个相当于求出了前缀和。加入修改操作后,就要用树状数组等来维护前缀和了。于是那个 “很好的性质” 又一次发挥了作用,由于主席树可以互相加减,所以可以用树状数组来套上它。做法和维护前缀和长得基本一样,不说了。
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> using namespace std; #define N int(2e5+10) int lson[N*20],rson[N*20],seg[N*20],tot,idx,root[N]; int num[N],Hash[N],n,m; void build(int L,int R,int &rt) { rt=++tot; seg[rt]=0; if(L==R)return ; int mid=(L+R)>>1; build(L,mid,lson[rt]); build(mid+1,R,rson[rt]); } void update(int last,int &rt,int L,int R) { rt=++tot; lson[rt]=lson[last]; rson[rt]=rson[last]; seg[rt]=seg[last]+1; if(R==L)return ; int mid=(R+L)>>1; if(idx<=mid)update(lson[last],lson[rt],L,mid); else update(rson[last],rson[rt],mid+1,R); } int query(int ss,int tt,int L,int R,int k) { if(L==R)return L; int mid=(R+L)>>1; int cnt=seg[lson[tt]]-seg[lson[ss]]; if(cnt>=k)return query(lson[ss],lson[tt],L,mid,k); else return query(rson[ss],rson[tt],mid+1,R,k-cnt); } int main() { int n,m; while(~scanf("%d%d",&n,&m)) { tot=0; for(int i=1;i<=n;i++) { scanf("%d",&num[i]); Hash[i]=num[i]; } sort(Hash+1,Hash+1+n); int cnt=unique(Hash+1,Hash+1+n)-Hash-1; build(1,cnt,root[0]); for(int i=1;i<=n;i++) { num[i]=lower_bound(Hash+1,Hash+1+cnt,num[i])-Hash; idx=num[i]; update(root[i-1],root[i],1,cnt); } int x,y,k; while(m--) { scanf("%d%d%d",&x,&y,&k); printf("%d\n",Hash[query(root[x-1],root[y],1,cnt,k)]); } } return 0; }