主席树本质上于线段树一样,都是维护一个区间,就我当前学到的深度来看,它主要是维护区间某个值的个数,对于原数组arr来说,他首先需要离散化,就是把数组内的相同元素用同一个数表示,然后建树的时候,只需要用他的下标建树,当前的这颗树继承它的上一颗树并且在当前下标所在的区间新建一颗子树,当前子树是继承上一颗子树的前提下,又加入了 当前下标,所以个数加一。而整个主席树的每一个线段树维护的是相同的区间,只是在区间内表示的元素的个数不同,在询问时,我们根据值是属于哪个区间而去递归调用,返回一个下标,(题目不同,询问方式有所不同)在hdu4417这个题目上,题目询问的是在【L,R】,这个区间内小于等于k的个数有几个,我们可以将题目转化一下,在数组trr中从左到右找到第一个大于k的数的下标x,而这个下标x往左所有的数都小于k,所以题目就变成了,在主席树中查询下标小于x的个数有几个,正巧,主席树所维护的刚好是在某个区间内下标的个数,这里有一点要注意的是:trr[x]>k的,不能等于,所以只要找下标小于x的个数即可。
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define siz 100005
using namespace std;
struct node{
int ls,rs,va;
}rt[siz*30];
int T[siz],arr[siz],trr[siz];
int n,m,q,tot;
void debug(int root,int l,int r){
//if()
cout<<rt[root].va<<endl;
if(l==r) return ;
int mid=(l+r)>>1;
debug(rt[root].ls,l,mid);
debug(rt[root].rs,mid+1,r);
}
void Init_hash(){
for(int i=1;i<=n;i++){
trr[i]=arr[i];
}
sort(trr+1,trr+1+n);
m=unique(trr+1,trr+1+n)-trr-1;
}
int build(int left,int right){
int root=tot++;
int tmp=root;
rt[root].va=0;
if(left!=right){
int mid=(left+right)>>1;
rt[root].ls=build(left,mid);
rt[root].rs=build(mid+1,right);
}
return tmp;
}
int hashx(int x){
return lower_bound(trr+1,trr+m+1,x)-trr;
}
int updata(int root,int pos,int val){
int newroot=tot++,tmp=newroot;
rt[newroot].va=rt[root].va+val;
int l=1,r=m;
while(l<r){
int mid=(l+r)>>1;
if(pos<=mid){
rt[newroot].ls=tot++;
rt[newroot].rs=rt[root].rs;
//rt[rt[newroot].rs].va=rt[rt[root].rs].va;
newroot=rt[newroot].ls;
root=rt[root].ls;
r=mid;
}
else{
rt[newroot].rs=tot++;
rt[newroot].ls=rt[root].ls;
//rt[rt[newroot].ls].va=rt[rt[root].ls].va;
newroot=rt[newroot].rs;
root=rt[root].rs;
l=mid+1;
}
rt[newroot].va=rt[root].va+val;
//cout<<pos<<" "<<trr[pos]<<" "<<rt[newroot].va<<endl;
}
return tmp;
}
int query(int left_root,int right_root,int h){
int l=1,r=m;
int ans=0;
while(l<r){///只考虑了小于情况
int mid=(l+r)>>1;
//cout<<l<<" "<<r<<endl;
if(h>mid){
// cout<<rt[rt[right_root].ls].va<<"**"<<rt[rt[left_root].ls].va<<endl;
ans+=rt[rt[right_root].ls].va-rt[rt[left_root].ls].va;
l=mid+1;
left_root=rt[left_root].rs;
right_root=rt[right_root].rs;
}
else{
r=mid;
left_root=rt[left_root].ls;
right_root=rt[right_root].ls;
}
}
if(l==r&&h>r){///将等于情况加入
ans+=rt[right_root].va-rt[left_root].va;
//break;
}
return ans;
}
void solve(){
while(q--){
int le,ri,h;
scanf("%d%d%d",&le,&ri,&h);
int k=hashx(h);///二分找下标trr[h]>=k
if(trr[k]==h) ++k;///trr[h]>k
//cout<<rt[T[le]].va<<" "<<rt[T[ri+1]].va<<endl;
//cout<<k<<" %%%"<<endl;
printf("%d\n",query(T[le],T[ri+1],k));///在区间【le+1,ri+1】中寻找,(题目是从0开始存数据的)
}
}
int main()
{
int t;
scanf("%d",&t);
for(int i=1;i<=t;i++){
tot=0;
scanf("%d%d",&n,&q);
for(int j=1;j<=n;j++){///存入1-n(题目是从0开始存数据的)
scanf("%d",&arr[j]);
}
Init_hash();
T[0]=build(1,m);
for(int j=1;j<=n;j++){
int pos=hashx(arr[j]);
T[j]=updata(T[j-1],pos,1);
}
//debug(T[10],1,m);
printf("Case %d:\n",i);
solve();
}
return 0;
}
/*
3
10 100
0 5 2 7 5 4 3 8 7 7
0 9 9
5 9 9
*/