划分树能求区间第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;
}