题目传送门//res tp hdu
目的
对长度为n的区间,m次询问,每次提供一个区间两端点与一个值x,求区间内不超过x的元素个数
n 1e5
m 1e5
ai [1,1e9] (i∈[1,n])
多测
数据结构与……?
划分树 + 二分查找
分析
建树0(nlogn)
单次查询第k小,需O(logn)
蛮力枚举区间内所有元素,查看是否大于x,需O(nlogn),m次询问,总O(mnlogn),显然会超时。
考虑以二分x的方式枚举出不超过x的最大上界只需O(mlogxlogn)。这种方法是可接受的。
#include<iostream>
#include<algorithm>
using namespace std;
const int L = 100010;
int tr[18][L],cnt[18][L],n,m,sor[L];
void build(int de,int l,int r){
if(l == r) return;
int mi = (l + r)>>1;
int scnt = mi - l + 1;
int M = sor[mi];
for(int i = l;i<=r;++i) if(tr[de][i] < M) scnt--;
int pl = l,pr = mi + 1;
for(int i = l,cntl = 0;i<=r;++i){
int num = tr[de][i];
if(num < M ||(num == M && scnt > 0)){
if(num == M) --scnt;
++cntl;
tr[de+1][pl++] = num;
}
else tr[de+1][pr++] = num;
cnt[de][i] = cntl;
}
build(de+1,l,mi);
build(de+1,mi+1,r);
}
int query(int de,int L,int R,int l,int r,int k){
if(L == R) return tr[de][L];
int mi = (L + R)>>1;
int left = 0,cnt_in_left = cnt[de][r];
if(L != l){
left = cnt[de][l-1];
cnt_in_left -= left;
}
if( k <= cnt_in_left ){
int newl = L + left;
int newr = newl + cnt_in_left - 1;
return query(de+1,L,mi,newl,newr,k);
}
else{
int a = l - L - left;
int b = r - l + 1 - cnt_in_left;
int newl = mi + 1 + a;
int newr = newl + b - 1;
return query(de+1,mi+1,R,newl,newr,k - cnt_in_left);
}
}
int Q(int l,int r,int h){
int lo = 1,hi = r - l + 1;
while(lo <= hi){
int mi = (lo + hi)>>1;
int M =query(0,1,n,l,r,mi);
if(h < M ) hi = mi-1;
else lo = mi + 1;
}
return --lo;
}
int main(){
int T,icase = 1,l,r,h,ans;
scanf(" %d",&T);
while(T--){
scanf(" %d %d",&n,&m);
for(int i = 1;i<=n;++i){
scanf(" %d",&sor[i]);
tr[0][i] = sor[i];
}
sort(sor+1,sor+1+n);
build(0,1,n);
printf("Case %d:
",icase++);
while(m--){
scanf(" %d %d %d",&l,&r,&h);
ans = Q(l+1,r+1,h);
printf("%d
",ans);
}
}
}