终于了解了最简单主席树- -
写代码犯了很SB的错误,调了半小时才看出来,难受
我学到的主席树是个支持查询历史版本的权值线段树
如何支持查询历史版本呢?
很容易想到每次更新的时候建一颗新的线段树
但显然空间爆炸
所以我们只需要新开那些需要更新的节点,不需要更新的节点直接拉到上一个版本即可
所以查[l,r]的k大值只需要查询第r个版本减去第l-1个版本的数据就行了
通常需要离散化一下
代码出乎意料的短。。。但是犯了个智障错误调了半个小时。。。
果然90%的时间都花在调试sb错误上
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#define For(i,l,r) for(int i=l;i<=r;++i)
#define MAXN (200010)
#define mid ((l+r)>>1)
using namespace std;
int read()
{
char c;
bool t=0;
int a=0;
while((c=getchar())==' '||c=='\n'||c=='\r');
if(c=='-')
{
t=1;
c=getchar();
}
while(isdigit(c))
{
a*=10;
a+=(c-'0');
c=getchar();
}
return a*(t?-1:1);
}
int ls[MAXN<<5],rs[MAXN<<5],sum[MAXN<<5];
int root[MAXN],cnt;
int n,m,a[MAXN],hash[MAXN],len,ans;
int getHash(const int &x)//离散化
{
return lower_bound(hash+1,hash+len+1,x)-hash;
}
void build(int l,int r,int x)
{
if(l==r)
return;
ls[x]=++cnt;
build(l,mid,cnt);
rs[x]=++cnt;
build(mid+1,r,cnt);
//printf("[l:%d r:%d x:%d ls:%d rs:%d]\n",l,r,x,ls[x],rs[x]);
}
void update(int w,int l,int r,int x,int his)//要修改的值 左端 右端 要修改的点 要修改的点的前一个版本
{
if(l==r)
{sum[x]=sum[his]+1;return;}//AAAA
ls[x]=ls[his];rs[x]=rs[his];sum[x]=sum[his]+1;
if(w<=mid)
{
ls[x]=++cnt;
update(w,l,mid,cnt,ls[his]);
}
else
{
rs[x]=++cnt;
update(w,mid+1,r,cnt,rs[his]);
}
//sum[x]=sum[ls[x]]+sum[rs[x]];
}
void ask(int l,int r,int rx,int lx,int k)//当前点左 当前点右 当前右点 当前左点 第k小
{
if(l==r)
{
ans=hash[l];
return;
}
if(k<=sum[ls[rx]]-sum[ls[lx]])//在左边
ask(l,mid,ls[rx],ls[lx],k);
else//在右边
ask(mid+1,r,rs[rx],rs[lx],k-sum[ls[rx]]+sum[ls[lx]]);
}
int main()
{
int tx,ty,tk;
n=read();m=read();
For(i,1,n)
a[i]=read();
memcpy(hash,a,sizeof a);
sort(hash+1,hash+n+1);
len=unique(hash+1,hash+n+1)-hash-1;
root[0]=++cnt;
build(1,len,cnt);
For(i,1,n)
{
root[i]=++cnt;
update(getHash(a[i]),1,len,cnt,root[i-1]);
}
For(i,1,m)
{
tx=read();ty=read();tk=read();
if(tx==ty)
{printf("%d\n",a[tx]);continue;}
ask(1,len,root[ty],root[tx-1],tk);
printf("%d\n",ans);
}
return 0;
}