hdu 4471 区间条件统计 区间 不超过 x 的元素的个数

题目传送门//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);
		}
	}
}



原文地址:https://www.cnblogs.com/tea-egg/p/11248877.html