划分树能求区间第k小值。
所以本题利用划分树+二分即可。
#include <iostream> #include <cstdio> #include <algorithm> using namespace std; #define N 100500 #define MID ((l+r)>>1) struct tree{ int s[N],t[20][N],num[20][N],n; void Build(int c,int l,int r) { int lm=MID-l+1,lp=l,rp=MID+1; for(int i=l;i<=MID;i++) lm-=s[i]<s[MID]; for(int i=l;i<=r;i++) { if( i==l ) num[c][i]=0; else num[c][i]=num[c][i-1]; if( t[c][i]==s[MID] ) { if( lm ) { lm--; num[c][i]++; t[c+1][lp++]=t[c][i]; } else t[c+1][rp++]=t[c][i]; } else if( t[c][i]<s[MID] ) { num[c][i]++; t[c+1][lp++]=t[c][i]; } else t[c+1][rp++]=t[c][i]; } if( l<r ) Build(c+1,l,MID),Build(c+1,MID+1,r); } int Query(int c,int l,int r,int ql,int qr,int k) { if( l==r ) return t[c][l]; int s,ss; if( l==ql ) s=0,ss=num[c][qr]; else s=num[c][ql-1],ss=num[c][qr]-num[c][ql-1]; if( k<=ss ) return Query(c+1,l,MID,l+s,l+s+ss-1,k); else return Query(c+1,MID+1,r,MID+1+ql-l-s,MID+1+qr-l-s-ss,k-ss); } void init(int* A,int Nn){ n = Nn; for(int i=1;i<=n;i++) s[i]=t[0][i]=A[i]; sort(s+1,s+1+n); Build(0,1,n); } int query(int l,int r,int k){ return Query(0,1,n,l,r,k); } }Tree; int a[N],n,m; int get_low(int l,int r,int v){ int x=1,y=r-l+1; while(x<y){ int mid = (x+y)>>1; if(Tree.query(l,r,mid)>=v) y=mid; else x=mid+1; } //求值v的最小次序。 if(Tree.query(l,r,x)<v) x++; return x; } int get_up(int l,int r,int v){ int x=1,y=r-l+1; while(x<y){ int mid = (x+y)>>1; if(Tree.query(l,r,mid)<=v) x=mid+1; else y=mid; } //求值v的最大次序的下一个点。 if(Tree.query(l,r,x)>v) x--; return x; } inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } int main() { int T,kase=1; scanf("%d",&T); while(T--){ n = read(); m = read(); for(int i=1;i<=n;i++) a[i] = read(); Tree.init(a,n); printf("Case #%d:\n",kase++); while(m--){ int l,r,x,y; l = read(); r = read(); x = read(); y = read(); int p1 = get_low(l,r,x); int p2 = get_up(l,r,y); if(p1>(r-l+1)||p2<1) printf("0\n"); else printf("%d\n",p2-p1+1); } } return 0; }