求区间[L,R]内小于等于k的数的个数。
划分树+二分
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
#define N 100005
int n;
int arr[N];//原数据,下标从1 开始
int sortedPos[N];//排序后
int lfnum[50][N];//元素所在区间的当前位置进入左孩子的元素的个数
int val[50][N];//记录第k 层当前位置的元素的值
bool cmp(const int &x,const int &y) {
return arr[x]<arr[y];
}
void build(int l,int r,int d) {
if(l==r) return;
int mid=(l+r)>>1,p=0;
for(int i=l; i<=r; i++) {
if(val[d][i]<=mid) {
val[d+1][l+p]=val[d][i];
lfnum[d][i]=++p;
} else {
lfnum[d][i]=p;
val[d+1][mid+i+1-l-p]=val[d][i];
}
}
build(l,mid,d+1);
build(mid+1,r,d+1);
}
//求区间[s,e]第k 大的元素
int query(int s,int e,int k,int l=1,int r=n,int d=0) {
if(l==r) return l;
int mid=(l+r)>>1,ss,ee;
ss=(s==l?0:lfnum[d][s-1]);
ee=lfnum[d][e];
if(ee-ss>=k) return query(l+ss,l+ee-1,k,l,mid,d+1);
return query(mid+1+(s-l-ss),mid+1+(e-l-ee),k-(ee-ss),mid+1,r,d+1);
}
int bin_find(int k,int left,int right) {
int l=1;
int r=right-left+1;
int mid=0;
while(l<=r) {
mid=(l+r)/2;
if(arr[sortedPos[query(left,right,mid)]]<=k) {
l=mid+1;
} else {
r=mid-1;
}
}
return l-1;
}
int main() {
int cas=1,m,l,r,k;
int T;
scanf("%d",&T);
while(T--) {
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++) {
scanf("%d",arr+i),sortedPos[i]=i;
}
sort(sortedPos+1,sortedPos+n+1,cmp);
for(int i=1; i<=n; i++) {
val[0][sortedPos[i]]=i;
}
build(1,n,0);
printf("Case %d:\n",cas++);
while(m--) {
scanf("%d%d%d",&l,&r,&k);
l++;
r++;
int mid=0;
if(arr[sortedPos[query(l,r,r-l+1)]]<=k) {
printf("%d\n",r-l+1);
continue;
}
if(arr[sortedPos[query(l,r,1)]]>k) {
printf("0\n");
continue;
}
mid=bin_find(k,l,r);
if(mid==0) {
puts("0");
continue;
}
// cout<<"**"<<mid<<endl;
while(1) {
int ans=arr[sortedPos[query(l,r,mid+1)]];
if(ans==k) {
mid++;
} else {
break;
}
}
printf("%d\n", mid);
}
}
return 0;
}
/*
1
10 10
0 5 2 7 5 4 3 8 7 7
2 8 6
3 5 0
1 3 1
1 9 4
0 1 0
3 5 5
5 5 1
4 6 3
1 5 7
5 7 3
*/