传送:主席树做法http://www.cnblogs.com/candy99/p/6160704.html
做那倒带修改的主席树时就发现分块可以做,然后就试了试
思想和教主的魔法差不多,只不过那个是求>=v的有几个
既然一个数v的名次可以求,我们二分这个数就行了啊
然而......
首先,你二分到的这个数不一定在区间里出现过
比如 1 2 5 8 9
4和5的名次都是3
于是,我修改了某个区间名次的定义:
“如果一个数的名次是x,但是区间中没有次数,那么他的名次为x-1”
实现上只需要find里return l-t+(b[l]==v) 等于说明出现过
然而
该死不写了鬼知道怎么回事用主席树就行了
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> using namespace std; typedef long long ll; const int N=1e5+500; inline int read(){ char c=getchar();int x=0,f=1; while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-'0'; c=getchar();} return x*f; } int n,Q,a[N],bl,m,b[N],pos[N]; int mp[N]; void reset(int x){ int l=(x-1)*bl+1,r=min(x*bl,n); for(int i=l;i<=r;i++) b[i]=a[i]; sort(b+l,b+r+1); } int flag; int find(int x,int v){ int l=(x-1)*bl+1,r=min(x*bl,n),t=l; while(l<=r){ int mid=(l+r)>>1; if(b[mid]<v) l=mid+1; else r=mid-1; } if(b[l]==v) flag=1; return l-t+1;//! } int rank(int l,int r,int v){ int ans=0; flag=0; if(pos[l]==pos[r]){ for(int i=l;i<=r;i++) if(a[i]<=v) ans++,flag=flag||a[i]==v; }else{ int t=pos[l]*bl; for(int i=l;i<=t;i++) if(a[i]<=v) ans++,flag=flag||a[i]==v; for(int i=(pos[r]-1)*bl+1;i<=r;i++) if(a[i]<=v) ans++,flag=flag||a[i]==v; for(int i=pos[l]+1;i<pos[r];i++) ans+=find(i,v); } if(!flag) ans--; return ans; } int query(int ql,int qr,int k){ int l=1,r=n; while(l<=r){ int mid=(l+r)>>1; int t=rank(ql,qr,mp[mid]); if(t<k) l=mid+1; else r=mid-1; } return l; } int main(){ freopen("in.txt","r",stdin); freopen("1.out","w",stdout); n=read();Q=read(); bl=sqrt(n); m=n/bl;if(n%bl) m++; for(int i=1;i<=n;i++) a[i]=mp[i]=read(),pos[i]=(i-1)/bl+1; for(int i=1;i<=m;i++) reset(i); sort(mp+1,mp+1+n); while(Q--){ int i=read(),j=read(),k=read(); printf("%d\n",mp[query(i,j,k)]); } }