主席树实质相当于每个前缀建立一颗线段树,因为相邻的前缀相比较只是差了一个数字,所以可以利用上一个前缀的线段树的有用信息来构成当前缀的线段树。
主席树查询静态区间第k大。
#include<stdio.h>
#include<algorithm>
#define N 100010
using namespace std;
struct TR{
int sum,l,r;
}tree[N*36]; //空间需要开36倍左右
int cnt;
void build(int l,int r,int &rt,int val)
{
tree[cnt++]=tree[rt]; //当前节点先复制上一颗线段树的节点
rt=cnt-1; //当前的节点编号
tree[rt].sum++; //受影响的节点sum++
if(l==r) return ;
int mid=(l+r)>>1;
if(val<=mid) build(l,mid,tree[rt].l,val);//当前添加的值在左子树
else build(mid+1,r,tree[rt].r,val); //当前添加的值在右子树
//printf("---%d %d %d\n",Tree[rt].L,Tree[rt].R,num);
}
int query(int i,int j,int k,int l,int r)
{
int d=tree[tree[j].l].sum-tree[tree[i].l].sum; //[1,i-1]和[1,j]区间的左子树节点数差值
if(l==r) return l;
int mid=(l+r)>>1;
if(k<=d) return query(tree[i].l,tree[j].l,k,l,mid); //左边查找第k大
else return query(tree[i].r,tree[j].r,k-d,mid+1,r);//左边有d个数,我们需要第k个,d<k所以需要在右边查找第k-d个
}
struct node{
int val,id;
}a[100005];
bool cmp(node q,node w)
{
return q.val<w.val;
}
int ran[100005];
int root[100005];
void init()
{
tree[0].l=tree[0].r=tree[0].sum=0;//初始化值未添加值线段树
cnt=1; //0节点是初始化没有添加任何数的线段树,所以编号从1开始。
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
init();
int n,m;scanf("%d%d",&n,&m);
/ 1
for(int i=1;i<=n;i++) scanf("%d",&a[i].val),a[i].id=i;
sort(a+1,a+n+1,cmp);
for(int i=1;i<=n;i++)
ran[a[i].id]=i;
// 2
//1到2的代码是把原数组的值离散化为1~n存到ran数组里。
for(int i=1;i<=n;i++)
{
root[i]=root[i-1];
build(1,n,root[i],ran[i]);
}
while(m--)
{
int l,r,k;
scanf("%d%d%d",&l,&r,&k);
printf("%d\n",a[query(root[l-1],root[r],k,1,n)].val);
}
}
return 0;
}
spoj-3267查询区间不同的数的个数
注意插入的是当前的数的下标i,而不是a[i].val,这样[l,r]区间不同的数的个数就相当于查找第r颗线段树区间中大于l-1的数的个数。
#include<stdio.h>
#include<algorithm>
#include <bits/stdc++.h>
#define N 100010
using namespace std;
struct TR{
int sum,l,r;
}tree[30010*36];
int cnt;
void build(int l,int r,int &rt,int pos,int val)
{
tree[cnt++]=tree[rt];
rt=cnt-1;
tree[rt].sum+=val;
int mid=(l+r)>>1;
if(l==r) return ;
if(pos<=mid) build(l,mid,tree[rt].l,pos,val);
else build(mid+1,r,tree[rt].r,pos,val);
return ;
}
int query(int pos,int rt,int l,int r)
{
if(l==r) return tree[rt].sum;
int mid=(l+r)>>1;
if(pos<mid) return query(pos,tree[rt].l,l,mid)+tree[tree[rt].r].sum;
else return query(pos,tree[rt].r,mid+1,r);
}
struct node{
int val,id;
}a[1000005];
int root[1000005];
int pre[1000005];
void init()
{
memset(pre,0,sizeof(pre));
tree[0].l=tree[0].r=tree[0].sum=0;//初始化值未添加值线段树
cnt=1; //0节点是初始化没有添加任何数的线段树,所以编号从1开始。
}
int main()
{
int n,m;
scanf("%d",&n);
init();
for(int i=1;i<=n;i++) scanf("%d",&a[i].val);
for(int i=1;i<=n;i++)
{
if(pre[a[i].val])
{
root[i]=root[i-1];
build(1,n,root[i],pre[a[i].val],-1);
build(1,n,root[i],i,1);
}
else
{
root[i]=root[i-1];
build(1,n,root[i],i,1);
}
pre[a[i].val]=i;
}
scanf("%d",&m);
while(m--)
{
int l,r;
scanf("%d%d",&l,&r);
printf("%d\n",query(l-1,root[r],1,n));
}
return 0;
}