主席树:

(不要管名字)

我们有的时候,会遇到很多种情况,对于每一种情况,都需要通过线段树的操作实现。

碰巧的是,相邻两种情况下的线段树的差异不大。(总体的差异次数是O(N)级别的,均摊就是O(常数)的了)

 

显然的是,我们不能对于每种情况都建造一棵线段树。n^n 空间直接MLE无疑。

救命稻草是:发现相邻两种情况下的线段树的差异不大。

所以,我们是否可以让不同的线段树共用同一个节点呢?!?!?

这就是主席树的本质。也是精妙之处所在。

代码实现不是很麻烦。

我一般用传返回值形式,每次返回一个节点编号,便于设置儿子编号。比较方便。

注意的是,我们必须记录lson,rson,不能采用x<<1,x<<1|1的形式。因为没有这样的规律可循。

你不知道子节点和自己有什么关系。(这是谁家的孩子?公家的)

 

 

经典例题:

1.区间第k小(大)。

离散化必须的。

对于每一个区间节点开一个权值线段树 。i的线段树的节点l~r表示,在真正的区间1~i中,大小在l~r的数出现的次数。

记录每个线段树节点根的所在位置。

查询的时候,l-1,r两棵线段树同时出发,区间[a,b]sum值做一个差,就是l~r这个区间内,数值在[a,b]之间的数的个数。

对于区间第k小,选择左儿子区间做差,u<k,就进入右儿子,同时k-=u

否则进入左儿子。

区间第k大正相反。

 

对于n棵主席树,相邻两个主席树i,i-1只在i的数值位置的值不一样。

所以,相邻的主席树只会增加logn个节点。

总空间复杂度nlogn

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int n,m;
int a[N],num[N];
int cnt,rt[N];
int li(int x){
    return lower_bound(num+1,num+cnt+1,x)-num;
}
struct node{
    int sum,lson,rson;
}t[N*18];
int tot;
int add(int x,int l,int r,int c){
    tot++;
    int ret=tot;
    t[tot].sum=t[x].sum+1;
    if(l==r){
        return ret;
    }
    int mid=(l+r)>>1;
    if(c<=mid){
        t[tot].rson=t[x].rson;
        t[tot].lson=add(t[x].lson,l,mid,c);
    } 
    else{
        t[tot].lson=t[x].lson;
        t[tot].rson=add(t[x].rson,mid+1,r,c);
    }
    return ret;
}
int query(int x,int y,int l,int r,int k){
    if(l==r){
        return l;
    }
    int mid=(l+r)>>1;
    int u=t[t[y].lson].sum-t[t[x].lson].sum;
    if(u>=k){
        return query(t[x].lson,t[y].lson,l,mid,k);
    }
    else{
        return query(t[x].rson,t[y].rson,mid+1,r,k-u);
    }
}
int main()
{
    scanf("%d%d",&n,&m);int df;
    for(int i=1;i<=n;i++) scanf("%d",&a[i]),num[++cnt]=a[i];
    sort(num+1,num+cnt+1);
    cnt=unique(num+1,num+cnt+1)-num-1;
    rt[0]=++tot;
    for(int i=1;i<=n;i++) rt[i]=add(rt[i-1],1,cnt,li(a[i]));
    int op,l,r;
    while(m--){
        scanf("%d%d%d",&l,&r,&op);
        int ot=query(rt[l-1],rt[r],1,cnt,op);
        printf("%d\n",num[ot]);
    }
    ret

相关文章:

  • 2021-10-18
  • 2022-02-23
  • 2022-02-15
  • 2021-07-21
  • 2021-10-14
  • 2022-12-23
猜你喜欢
  • 2022-01-17
  • 2021-12-06
  • 2021-10-08
  • 2022-12-23
  • 2021-08-17
  • 2022-01-13
相关资源
相似解决方案