题目地址:http://acm.split.hdu.edu.cn/showproblem.php?pid=4417
思路:
1.查询区间内不大于x的数的个数。
2.使用主席树求区间k大值,二分k,求最后一个不大于x的k大值,则k即为元素个数。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define lson l,m
#define rson m+1,r
#define debug
using namespace std;
const int maxn=2000000+50;
int T[maxn];
int n,m,tot,cnt;
int a[maxn],b[maxn];
int L[maxn],R[maxn],sum[maxn];
int build(int l,int r)
{
int root=++tot;
sum[root]=0;
if(l<r)
{
int m=(l+r)>>1;
L[root]=build(lson);
R[root]=build(rson);
}
return root;
}
int update(int pre,int l,int r,int x)
{
int root=++tot;
sum[root]=sum[pre]+1;
L[root]=L[pre],R[root]=R[pre];
if(l<r)
{
int m=(l+r)>>1;
if(x<=m)
{
L[root]=update(L[pre],lson,x);
}
else
{
R[root]=update(R[pre],rson,x);
}
}
return root;
}
int query(int u,int v,int l,int r,int k)
{
if(l>=r) return l;
int num=sum[L[v]]-sum[L[u]];
int m=(l+r)>>1;
if(num>=k)
{
query(L[u],L[v],lson,k);
}
else
{
query(R[u],R[v],rson,k-num);
}
}
int solve(int l,int r,int x)
{
int L=1,R=r-l+1,ans=0;
while(L<=R)
{
int mid=(L+R)>>1;
int tmp=query(T[l-1],T[r],1,cnt,mid);
if(b[tmp]<=x)
{
L=mid+1;
ans=mid;
}
else
{
R=mid-1;
}
}
return ans;
}
int main()
{
#ifdef debu
freopen("in.txt","r",stdin);
#endif // debug
int t,cas=0;
scanf("%d",&t);
while(t--)
{
printf("Case %d:\n",++cas);
tot=0;
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++)
{
scanf("%d",&a[i]);
b[i]=a[i];
}
sort(b+1,b+n+1);
cnt=unique(b+1,b+n+1)-(b+1);
T[0]=build(1,cnt);
for(int i=1; i<=n; i++)
{
int x=lower_bound(b+1,b+cnt+1,a[i])-b;
T[i]=update(T[i-1],1,cnt,x);
}
for(int i=1; i<=m; i++)
{
int l,r,x;
scanf("%d%d%d",&l,&r,&x);
l++,r++;
printf("%d\n",solve(l,r,x));
}
}
return 0;
}