主席树是可持久化线段树,所以它是线段树的可持久化的版本.
首先你需要会权值线段树,就是对每一个值建一棵线段树,节点里存储这个值出现的范围.
如果用普通的建法直接就爆炸了,所以你需要动态开点.
什么是动态开点呢?说白了也很简单.
普通的线段树的两个儿子不是 x < < 1 x<<1 x<<1和 x < < 1 ∣ 1 x<<1|1 x<<1∣1吗?
动态开点的时候,我们要记录 l s [ x ] ls[x] ls[x]和 r s [ x ] rs[x] rs[x].
当我们建到一个点的时候,我们给它直接分配一个编号.
代码如下.
如果是以洛谷模板线段树1为例:
const int yuzu=2e5;
typedef int karen[yuzu<<5|13];
karen val,lazy,ls,rs; // 和普通的线段树不一样的是,ls和rs不是固定的,要开两个数组记录.
void build(int &rt,int l,int r) {
if (!rt) rt=++cnt; // cnt是当前记录到的节点编号.
if (l==r) val[rt]=a[l];
else {
int mid=l+r>>1;
build(ls[rt],l,mid);
build(rs[rt],mid+1,r);
push_up(rt);
} // 其他的部分都差不多.
}
现在你大概懂了.
接下来我们进入主席树的学习.
我们对每一个节点的前缀建一棵线段树,节点存储 l , r l,r l,r值域区间中在 1 → i 1\to i 1→i有多少个数.
如果每次都这样建就炸了,所以不行.
但是我们发现 i i i和 i − 1 i-1 i−1的两个点建的前缀线段树只有 l o g n logn logn个点是不同的(每一次更新显然更新 l o g n logn logn个节点,因为每一次对值域二分).
那么我们可以让第 i i i个点和第 i − 1 i-1 i−1个点的线段树共用左儿子或者右儿子,这样每次新加的节点的个数就是 l o g n logn logn的,所以空间复杂度就对了.
我们接下来写新建的线段树.
int udt(int pre,int l,int r,int k) { // 基于pre这个节点新建一棵树.
int rt=++cnt,mid=l+r>>1; // 开一个新节点
le[rt]=le[pre],ri[rt]=ri[pre],sum[rt]=sum[pre]+1;
/*先共用左儿子右儿子,存储值的个数多一个.*/
if (l^r) k<=mid?le[rt]=udt(le[pre],l,mid,k):ri[rt]=udt(ri[pre],mid+1,r,k);
/*看插入权值的位置决定是更新左孩子还是右孩子*/
return rt;
}
然后是询问,我们发现对于 l − 1 , r l-1,r l−1,r两棵线段树来说,这是前缀和建的线段树,大胆猜想,它们有可加减性!
我记得有线段树合并,那是线段树相加,这里线段树当然也可以相减!
那么可以写出询问函数.
int query(int u,int v,int l,int r,int k) { // 询问用的是二分的模式
if (l>=r) return l; // 如果l=r,答案就是l了.
int x=sum[le[v]]-sum[le[u]],mid=l+r>>1;
/*我们看看左半部分有多少个数较小.*/
return x>=k?query(le[u],le[v],l,mid,k):query(ri[u],ri[v],mid+1,r,k-x);
/*如果左半部分有大于等于k个数字,询问左半部分,否则询问右半部分.注意k询问右半部分的时候要减去x.*/
}
这样子就搞定了.
最后给出代码,谢谢大家.
#include //Ithea Myse Valgulious
namespace chtholly{
typedef long long ll;
#define re0 register int
#define rel register ll
#define rec register char
#define gc getchar
//#define gc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<23,stdin),p1==p2)?-1:*p1++)
#define pc putchar
#define p32 pc(' ')
#define pl puts("")
/*By Citrus*/
char buf[1<<23],*p1=buf,*p2=buf;
inline int read(){
int x=0,f=1;char c=gc();
for (;!isdigit(c);c=gc()) f^=c=='-';
for (;isdigit(c);c=gc()) x=(x<<3)+(x<<1)+(c^'0');
return f?x:-x;
}
template <typename mitsuha>
inline bool read(mitsuha &x){
x=0;int f=1;char c=gc();
for (;!isdigit(c)&&~c;c=gc()) f^=c=='-';
if (!~c) return 0;
for (;isdigit(c);c=gc()) x=(x<<3)+(x<<1)+(c^'0');
return x=f?x:-x,1;
}
template <typename mitsuha>
inline int write(mitsuha x){
if (!x) return 0&pc(48);
if (x<0) pc('-'),x=-x;
int bit[20],i,p=0;
for (;x;x/=10) bit[++p]=x%10;
for (i=p;i;--i) pc(bit[i]+48);
return 0;
}
inline char fuhao(){
char c=gc();
for (;isspace(c);c=gc());
return c;
}
}using namespace chtholly;
using namespace std;
const int yuzu=2e5;
typedef int fuko[yuzu|10];
typedef int karen[yuzu<<5];
fuko gen,a,b;
struct chairman_tree{
#define ls le[rt],l,mid
#define rs ri[rt],mid+1,r
karen le,ri,sum; int cnt;
void build(int &rt,int l,int r) {
if (!rt) rt=++cnt;
sum[rt]=0; int mid=l+r>>1;
if (l^r) build(ls),build(rs);
}
int udt(int pre,int l,int r,int k) {
int rt=++cnt,mid=l+r>>1;
le[rt]=le[pre],ri[rt]=ri[pre],sum[rt]=sum[pre]+1;
if (l^r) k<=mid?le[rt]=udt(le[pre],l,mid,k):ri[rt]=udt(ri[pre],mid+1,r,k);
return rt;
}
int query(int u,int v,int l,int r,int k) {
if (l>=r) return l;
int x=sum[le[v]]-sum[le[u]],mid=l+r>>1;
return x>=k?query(le[u],le[v],l,mid,k):query(ri[u],ri[v],mid+1,r,k-x);
}
}my_;
int main() {
int i,n,q;
read(n),read(q);
for (i=1;i<=n;++i) a[i]=b[i]=read();
sort(b+1,b+n+1);
int m=unique(b+1,b+n+1)-b-1;
my_.build(*gen,1,m);
for (i=1;i<=n;++i)
gen[i]=my_.udt(gen[i-1],1,m,lower_bound(b+1,b+m+1,a[i])-b);
for (;q--;) {
int l=read(),r=read(),v=read();
write(b[my_.query(gen[l-1],gen[r],1,m,v)]),pl;
}
}