学习记录5:主席树

8 篇文章 0 订阅
2 篇文章 0 订阅

嗯 昨天某题TLE了一晚上 

搜索优化方式的时候看到了这个 

于是 今天学了下 - -


点击打开链接

点击打开链接

觉得上面那个教程的图蛮好的 

点击打开链接


这些讲的挺详细的 


嗯 说下理解 也不知道是不是对的  = =

错了话 继续改好了 先记录一下  ^^


主席树的主体是一棵线段树 

有点像是一堆树被拼在一起  共同使用某些部分


嗯 先画张图




这个是一棵空的线段树


假设我们有一个数列 ARR[N] = { 1,2,8,3,7,6,5,4} 

现在要查询 某段区间 [l,r]中值小于K的元素的个数 其中集合元素的大小 [1~n]

这个时候 如果数据量较大的时候直接扫一般会超时

但是我们可以这样考虑 

如果 我们构建N棵线段树 第i棵线段树以arr[1] ~~arr[ i] 的元素建立

这样 关于统计这个区间值小于K 的问题就可变为  NUM = 第 r 棵线段树 的 区间 [1,k ] 元素数量 - 第 l - 1 棵线段树的 区间 [1,k ] 元素数量

但是 这样一来 可以发现 很容易 MLE 

于是 我们可以分析一下 

会发现 这里的每棵线段树的形态都是一样的 (因为维护的都是 arr 的元素值的个数)

这样以来 可以对着图看

我们现在构建第一棵线段树

粗线条表示的是每棵线段树将会在空树基础上统计更改的路径  


接着是第二棵


第三棵


等等等

通过这几张图 

可以发现 第 i 棵线段树的数值 实际上相当于是 在第 i - 1 棵线段上完成更新的结果

而我们并不能直接在第 i - 1 棵树上直接更新 (直接更新完之后 第 i - 1棵线段树的数值就不准确了)

那么 通过观察 可以发现 第 i 棵线段树 仅 和 第 i - 1 棵线段树 有一条链上的不同

那么我们就可以 设想 在建立第 i 棵线段树上 并不完全的新建一个   可以 与上棵线段树共用除了要更新的那条链上的节点


就像这张图  其中 i = 2




灰色的是 i = 2 的线段树 用 16  17 18 19 代替原来的紫色路径上的点 (但是节点的区间意义是一样的)(说的是更上面的 第 i = 2 建立的线段树)

而其他颜色的路径不需要更新 可以利用  就像新建立的代表 [1,4] 的节点 17 它的右区间没有更新 

他就可以使用 代表[3,4]区间的 5 号节点  也就是他的右子树 还是 5 号结点


再下一棵线段树就在这棵树的基础上做连接判断 

等等等 

(这种树 用数组模拟指针其实也蛮方便的 (- -.))

 

这样每次建树 最多只要 加上根节点开始的最大深度的那条链的大小 优化了空间 


这大概是 主席树的大致形态 


然后使用的话  


这个是统计 第 i 棵树 [l,r]之间的元素个数的


#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cctype>
#include<cstdio>
#include<string>
#include<cstring>
#include<vector>
#include<set>
#include<map>
#include<queue>
#include<cmath>
#include<bitset>
#define pi acos(-1.0)
#define inf 1<<29
#define INF 0x3fffffff
#define zero 1e-8

const int li[]={-1,0,1,0};
const int lj[]={0,-1,0,1};

using namespace std;

const int N= 1e5+107;

int arr[N];

struct lo{
    int l,r,data,node;
    void show(){
        cout<<"l: "<<l<<" r: "<<r<<" data: "<<data<<" node: "<<node<<endl;
    }
}cmtree[N*100];

int root[N],ROOT;
int pt;
int sum;

void init(){
    memset(cmtree,0,sizeof(cmtree));
    pt=0;
}

void build(int from,int node,int left,int right,int arrow){
    ++pt;
    int now=pt;

    if (arrow==1) cmtree[from].l=pt;
    else cmtree[from].r=pt;
    cmtree[pt].node=node;
    if (left==right){
        cmtree[pt].l=cmtree[pt].r=-1;
        cmtree[pt].data=0;
        return;
    }
    build(now,node*2,left,(left+right)/2,1);
    build(now,node*2+1,(left+right)/2+1,right,2);
    return;
}

void FIND(int node,int left,int right,int next){

    cout<<" left: "<<left<<" right: "<<right<<endl<<"-----------"<<endl;
    cmtree[next].show();
    cout<<endl<<endl;
    if (left==right) return ;
    FIND(node*2,left,(left+right)/2,cmtree[next].l);
    FIND(node*2+1,(left+right)/2+1,right,cmtree[next].r);
}
void update(int node,int left,int right,int from,int mod,int data,int arrow){
    ++pt;
    int now=pt;
    if (arrow==1) cmtree[from].l=pt;
    else cmtree[from].r=pt;
    int mid=(left+right)/2;
    cmtree[pt].node=node;
    if (left==right){
        cmtree[pt].data=cmtree[mod].data+1;
        cmtree[pt].l=cmtree[pt].r=-1;
        return;
    }

    if (data<=mid) {
        update(node*2,left,mid,pt,cmtree[mod].l,data,1);
        cmtree[now].r=cmtree[mod].r;
    }

    else {
        update(node*2+1,mid+1,right,pt,cmtree[mod].r,data,2);
        cmtree[now].l=cmtree[mod].l;
    }
    cmtree[now].data=cmtree[cmtree[now].l].data+cmtree[cmtree[now].r].data;
    ROOT=now;
}

void query(int node,int left,int right,int Begin,int End,int next){
    if (left>End||right<Begin) return ;
    if (left>=Begin&&right<=End) {
        sum+=cmtree[next].data;
        return;
    }
    int mid=(left+right)/2;
    query(node*2,left,mid,Begin,End,cmtree[next].l);
    query(node*2+1,mid+1,right,Begin,End,cmtree[next].r);
}
int main()
{
  int n;
  while(~scanf("%d",&n)){
    init();
    for (int i=1;i<=n;++i){
        scanf("%d",&arr[i]);
    }
    pt=0;
    root[0]=pt;
    build(root[0],1,1,n,1);
    for (int i=1;i<=n;++i){
        root[i]= ++pt;
        update(1,1,n,root[i],root[i-1]+1,arr[i],1);
    }

    int q;
    scanf("%d",&q);
    for (int i=0;i<q;++i){
        int l,r,k;
        sum=0;
        scanf("%d %d %d",&l,&r,&k);
        query(1,1,n,l,r,root[k]+1);
        int ans=sum;
        cout<<ans<<endl;
    }
  }
  return 0;
}


嗯 最后感觉 数据结构的使用 大概是把问题处理成某种状态后再使用数据结构优化


这有一题可以看看

hdu 5790 Prefix

题目链接 点击打开链接

AC代码 点击打开链接




 


 








  












  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值