主席树一般是用来记录没个数的出现次数,类似权值线段树,与普通线段树不同的是它每次只用更新一条链。
不能再用堆存储,但是可以根据根结点编号定位,建立l,r,rts数组
rts存每个位置的根节点编号,快速定位,l,r数组存储该根节点编号的左右儿子编号
add操作写法变化不大,query写法看情况,目前有求第K大与求和两种写法
第一题是递归找区间第K大,第二题是求区间出现次数和,第三题前两题综合,第四题和第三题差不多但在线操作,不用离散化
第一题比较特殊,只用找出第K大,因此顺下去找就行了
但有些要求区间数字出现次数和,要么版本相减,要么可以加个pushup操作(回溯)
感谢奇奇的模板
#include<cstdio>
#include<algorithm>
using namespace std;
#define lson l,m,ls[rt]
#define rson m+1,r,rs[rt]
typedef long long ll;
const int maxn = 1e5 + 5;
int ls[maxn*30],rs[maxn*30],tot,rts[maxn];//tot为总结点数 ,rts为每个位置的线段树的根
int t[maxn*30];//重点!权值线段树,存的是每个数出现次数
int a[maxn],num[maxn];//原数组和离散化的
void build(int l,int r,int &rt)
{
rt = ++tot;//第几个结点
t[rt] = 0;
if(l==r)
return ;
int m = l+r>>1;
build(lson);
build(rson);
}
void add(int p,int C,int l,int r,int &rt,int lst)//rt是引用调用
{
rt = ++tot;
ls[rt] = ls[lst],rs[rt] = rs[lst];
t[rt] = t[lst] + C; //上一个版本的结点拷贝信息,一般C=1,表示出现次数+1
if(l==r)
return ;
int m = l+r>>1;
if(p<=m)
add(p,C,lson,ls[rt]);
else
add(p,C,rson,rs[rt]);
}
int query(int x,int y,int l,int r,int k)
{
if(l==r)
return l;
int c = t[ls[y]] - t[ls[x]];//c表示左子树中数字的数量
int m = l+r>>1;
if(c>=k)
return query(ls[x],ls[y],l,m,k);
else //左子树不够k,从右子树找
return query(rs[x],rs[y],m+1,r,k-c);
}
int N,M,c,x,y,k;
int main()
{
while(scanf("%d%d",&N,&M)!=EOF)
{
tot = 0;
for(int i=1;i<=N;++i)
{
scanf("%d",&a[i]);
num[i] = a[i];
}
sort(num+1,num+N+1);
int cnt = unique(num+1,num+N+1)-(num+1);
build(1,cnt,rts[0]);
for(int i=1;i<=N;++i)
a[i] = lower_bound(num+1,num+cnt+1,a[i])-num;//离散化
for(int i=1;i<=N;++i)
add(a[i],1,1,cnt,rts[i],rts[i-1]);
while(M--)
{
scanf("%d%d%d",&x,&y,&k);
printf("%d\n",num[query(rts[x-1],rts[y],1,cnt,k)]);
}
}
return 0;
}
不同种类和,同种只能算一次
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define lson l,mid,ls[rt]
#define rson mid+1,r,rs[rt]
const int maxn=1e5+10;
int a[300005];
int num[300005];
int ls[maxn*30],rs[maxn*30],rts[maxn];
int t[maxn*30];
int vis[maxn];//额外记录上一个重复元素位置
int tot,ans;
void build(int l,int r,int &rt)
{
rt=++tot;
t[rt]=0;
if(l==r)return;
int mid=l+r>>1;
build(lson);
build(rson);
}
void add(int p,int C,int l,int r,int &rt,int lst)
{
rt=++tot;
ls[rt]=ls[lst],rs[rt]=rs[lst];
t[rt]=t[lst]+C;
if(l==r)return;
int mid=l+r>>1;
if(p<=mid)
add(p,C,lson,ls[rt]);
else
add(p,C,rson,rs[rt]);
}
void query(int x,int y,int l,int r,int rt)//注意这里写法
{
if(l==x)
{
// return t[rt];
ans+=t[rt];
return ;
}
//int c=t[ls[y]]-t[ls[x]];
int mid=l+r>>1;
if(x<=mid)
ans+=t[rs[rt]],query(x,y,lson);
// if(y>mid)
else
query(x,y,rson);
}
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
num[i]=a[i];
}
sort(num+1,num+n+1);
int cnt = unique(num+1,num+n+1)-(num+1);
// build(1,cnt,rts[0]);
for(int i=1;i<=n;++i)
a[i] = lower_bound(num+1,num+cnt+1,a[i])-num;
int tmp;
for(int i=1;i<=n;i++){ //这里要考虑重复元素 ,重复的话只保留最后一个
if(!vis[a[i]])
add(i,1,1,n,rts[i],rts[i-1]);
else{
add(vis[a[i]],-1,1,n,tmp,rts[i-1]);//先删除再添加
add(i,1,1,n,rts[i],tmp);
}
vis[a[i]]=i;
}
int q,x,y;
cin>>q;
while(q--)
{
ans=0;
scanf("%d%d",&x,&y);
query(x,y,1,n,rts[y]);
printf("%d\n",ans);
}
}