题目大意:n个数,给定一段区间【l, r】,问在这段区间上小于等于 h 的数字有多少个。
解题思路:采用划分树可以在O(log(n))求出区间第K大的值,再采用二分法选择出最大的K即为答案。划分树的讲解会在以后补充。
AC代码:
#include<stdio.h>
#include<algorithm>
using namespace std;
#define M 100005
int tree[20][M],sorted[M];
int toLeft[20][M];
void build(int level,int left,int right){
if(left==right)return ;
int mid=(left+right)>>1;
int i;
int suppose;//假设在中位数sorted[mid]左边的数都全部小于sorted[mid]
suppose=mid-left+1;
for(i=left;i<=right;i++){
if(tree[level][i]<sorted[mid]){
suppose--;
}
}
//如果suppose==1,则说明数组中值为sorted[mid]只有一个数。比如序列:1 3 4 5 6,sorted[mid]=4
/*如果suppose>1,则说明数组中左半边值为sorted[mid]的不止一个数,为mid-suppose。比如序列:1 4 4 4 6,sorted[mid]=4
*
* */
int lpos=left,rpos=mid+1;
for(i=left;i<=right;i++){
if(i==left){//这里是预处理,相当与初始化
toLeft[level][i]=0;
}else{
toLeft[level][i]=toLeft[level][i-1];
}
if(tree[level][i]<sorted[mid]){//划分到中位数左边
toLeft[level][i]++;
tree[level+1][lpos++]=tree[level][i];
}else if(tree[level][i]>sorted[mid]){//划分到中位数右边
tree[level+1][rpos++]=tree[level][i];
}else{//这里,suppose大于0的数划分到中位数的左边
if(suppose!=0){//这里的处理太巧妙了!帅气!
suppose--;
toLeft[level][i]++;
tree[level+1][lpos++]=tree[level][i];
}else{//表示
tree[level+1][rpos++]=tree[level][i];
}
}
}
build(level+1,left,mid);
build(level+1,mid+1,right);
}
//在[left,right]数据中查询[qleft,qright]中第k大的数据
int query(int level,int left,int right,int qleft,int qright,int k){
if( qleft==qright)
return tree[level][qleft];
int s;//代表[left,qleft)之间有多个个元素被分到左边
int ss;//[qleft, qright]内将被划分到左子树的元素数目
int mid=(left+right)>>1;
if(left==qleft){
s=0;
ss=toLeft[level][qright];
}else{
s=toLeft[level][qleft-1];
ss=toLeft[level][qright]-s;
}
int newl,newr;
if(k<=ss){//查询左边
newl=left+s;
newr=left+s+ss-1;
return query(level+1,left,mid,newl,newr,k);
}else{//查询右边
newl=mid-left+1+qleft-s;
newr=mid-left+1+qright-s-ss;
return query(level+1,mid+1,right,newl, newr,k - ss);
}
}
int solve(int n,int ql, int qr, int h)
{
int l = 1, r = qr-ql+1;
int mid = (l+r)>>1;
int ans = 0;
while(l<=r)
{
int val = query(0,1,n,ql,qr,mid);
if(val<=h)
{
ans = mid;
l = mid;
}
else
r = mid -1;
mid = (l+r)>>1;
//printf("%d %d %d\n",l,r,mid);
}
return ans;
}
int main(){
int n,m;
int T;
scanf("%d",&T);
for(int cnt = 1; cnt<=T; cnt++){//N个数
printf("Case %d:\n",cnt);
scanf("%d%d",&n,&m);
int i;
for(i=1;i<=n;i++){
scanf("%d",&tree[0][i]);
sorted[i]=tree[0][i];
}
sort(sorted+1,sorted+n+1);
build(0,1,n);
/*for(i=0;i<n;i++){
for(int j=1;j<=n;j++){
printf("%d ",toLeft[i][j]);
}
printf("\n");
}*/
//scanf("%d",&m);//M组询问
int ql,qr,h;//该段区间从小到大的第K个数
for(i=0;i<m;i++){
scanf("%d %d %d",&ql,&qr,&h);
ql++;qr++;
printf("%d\n",solve(n,ql,qr,h));
}
}
return 0;
}