这两天一直在看各种树~划分树,左偏树,主席树,伸展树~~~好乱~~

一听到主席树这个名字的时候感觉好奇怪,为什么会叫主席树,感觉好难好高大上,所以一直敬而远之,,,,,主席树是一个大牛的拼音缩写HJT(hu jingtao),额~~大牛就是厉害!!!

昨晚上bili发现了一个主席树视频,突然感觉好开心,终于有教程了,哈哈哈~~~发现up主还上传了其他 的算法教程,,,,于是叫注册了bili账号还百度通过了正式会员~~

哈哈~~本来以为b站考试很难,,的确很难(因为之前听朋友说她好艰难才通过、的),但是有百度这种东西~~今天看了up主关于主席树的讲解,虽然明白主席树的什么,但是要手动实现就不会了,看了up的代码好久才渐渐明白,up主的代码好短而已,但是技巧之类的好多,特别是到递归,我觉得递归真的好恶心,因为很难捕捉一下就跳到哪里去了,特别是带返回值的。。。。。后来又看了kuangbin的代码,自己又写了一遍,但是Kuangbin的不是递归写的,而我改成了递归的,不知为什么感觉有点成就感,哈哈~至少对代码的理解又加深了一点点。,,好怕过两天就忘的一干二净了,。。

进入正题:

up主连接:主席树       (一开始是通过up的视频学的)

下面是摘抄其他人的:

主席树的主体是线段树,准确的说,是很多棵线段树,存的是一段数字区间出现次数(所以要先离散化可能出现的数字)。举个例子,假设我每次都要求整个序列内的第 k 小,那么对整个序列构造一个线段树,然后在线段树上不断找第 k 小在当前数字区间的左半部分还是右半部分。这个操作和平衡树的 Rank 操作一样,只是这里将离散的数字搞成了连续的数字。

先假设没有修改操作:

对于每个前缀 S1…i,保存这样一个线段树 Ti,组成主席树。这样不是会 MLE 么?最后再讲。

注意,这个线段树对一条线段,保存的是这个数字区间的出现次数,所以是可以互相加减的!还有,由于每棵线段树都要保存同样的数字,所以它们的大小、形态也都是一样的!这实在是两个非常好的性质,是平衡树所不具备的。

对于询问 (i,j),我只要拿出 Tj 和 Ti-1,对每个节点相减就可以了。说的通俗一点,询问 i..j 区间中,一个数字区间的出现次数时,就是这些数字在 Tj 中出现的次数减去在 Ti-1 中出现的次数。

那么有修改操作怎么办呢?

如果将询问看成求一段序列的数字和,那么上面那个相当于求出了前缀和。加入修改操作后,就要用树状数组等来维护前缀和了。于是那个 “很好的性质” 又一次发挥了作用,由于主席树可以互相加减,所以可以用树状数组来套上它。做法和维护前缀和长得基本一样,不说了。

这段指出了主席树的主要性质。。

  • 线段树的每个结点,保存的是这个区间含有的数字的个数。
  • 主席树的每个结点,也就是每颗线段树的大小和形态也是一样的,也就是主席树之间可以相互进行加减运算。。

同时我们也枚举一下主席树的一些局限:

  • 主席树是一种离线结构。。(必须预先知道所有数字的范围。。这在一些应用中会成为障碍。。。
  • 存在 MLE 问题。。(如果按照定义里面的方法去写,对每个结点都需要开一整可线段树,至少都是 O(n2) 级别的空间。。

——————————————————
开始填坑。由于每棵线段树的大小形态都是一样的,而且初始值全都是 0,那每个线段树都初始化不是太浪费了?所以一开始只要建一棵空树即可。

然后是在某棵树上修改一个数字,由于和其他树相关联,所以不能在原来的树上改,必须弄个新的出来。难道要弄一棵新树?不是的,由于一个数字的更改只影响了一条从这个叶子节点到根的路径,所以只要只有这条路径是新的,另外都没有改变。比如对于某个节点,要往右边走,那么左边那些就不用新建,只要用个指针链到原树的此节点左边就可以了,这个步骤的前提也是线段树的形态一样。

假设s是数字个数,这个步骤的空间复杂度显然是 O(logs)。用树状数组去套它,共有 2logn 棵树被修改,m 个操作再加上一开始的空树和 n 个数字,总共就是 O((n+m)lognlogs)。Fotile 大神说如果加上垃圾回收的话,可以去掉一个 log…… ym

poj 2104 k-th number

这题也可以用划分树(见上一篇)

代码1:up主  代码2: kuangbin修改版     代码3:kuangbin

版本1

#include <cstdio>
#include <iostream>
#include <cstring>
#include <queue>
#include <cmath>
#include <algorithm>
using namespace std;
const int maxn=1e5+6;
int n,m,cnt=0,root[maxn],a[maxn],x,y,k;
struct node{int l,r,sum;
}T[maxn*40];
vector<int>v;int getid(int x)  {return lower_bound(v.begin(),v.end(),x)-v.begin()+1;
}void update(int l,int r,int &x,int y,int pos){T[++cnt]=T[y],T[cnt].sum++,x=cnt;if(l==r) return ;int mid=(l+r)/2;if(mid>=pos) update(l,mid,T[x].l,T[y].l,pos);else update(mid+1,r,T[x].r,T[y].r,pos);
}int query(int l,int r,int x,int y,int k){if(l==r) return l;int mid=(l+r)/2;int sum=T[T[y].l].sum-T[T[x].l].sum;if(sum>=k) return query(l,mid,T[x].l,T[y].l,k);else return query(mid+1,r,T[x].r,T[y].r,k-sum);
}
int main() {#ifndef ONLINE_JUDGEfreopen("in.txt","r",stdin);#endifscanf("%d%d",&n,&m);for(int i=1;i<=n;i++) scanf("%d",&a[i]),v.push_back(a[i]);sort(v.begin(),v.end());/*unique只是把重复的元素放到容器的后面,而它本身会返回一个迭代器,只向这些元素的开始部分。因此要向真正删除这些元素,还是要“手工”处理一下。可以用vector的erase*/v.erase(unique(v.begin(),v.end()),v.end());       //去重for(int i=1;i<=n;i++) update(1,n,root[i],root[i-1],getid(a[i]));//for(int i=0;i<=n;i++) cout<<root[i]<<" ";for(int i=1;i<=m;i++){scanf("%d%d%d",&x,&y,&k);printf("%d\n",v[query(1,n,root[x-1],root[y],k)-1]);} }                        

版本2

#include<cstdio>
#include<iostream>
#include <algorithm>
#include <map>
#include <cmath>
using namespace std;
const int maxn=1e5+10;
const int N=maxn*30;
int n,q,m,tot;
int a[maxn],t[maxn];
int T[N],ls[N],rs[N],c[N];void init()
{for(int i=1;i<=n;i++)t[i]=a[i];sort(t+1,t+n+1);/*在STL中unique函数是一个去重函数, unique的功能是去除相邻的重复元素(只保留一个),其实它并不真正把重复的元素删除,是把重复的元素移到后面去了,然后依然保存到了原数组中,然后 返回去重后最后一个元素的地址,去重后最后一个元素的下一个地址 因为unique去除的是相邻的重复元素,所以一般用之前都会要排一下序。*/m=unique(t+1,t+n+1)-t-1;
}int build(int l,int r)
{int root=tot++;c[root]=0;if(l!=r){int mid=(l+r)/2;ls[root]=build(l,mid);rs[root]=build(mid+1,r);}return root;
}int hash(int x)
{return lower_bound(t+1,t+1+m,x)-t;   //返回下标
} int update(int root,int pos,int val,int l,int r)
{int newroot=tot++,tmp=newroot;c[newroot]=c[root]+val;if(l==r) return 1;int mid=(l+r)/2;if(pos<=mid){ls[newroot]=tot;rs[newroot]=rs[root];update(ls[root],pos,val,l,mid);}else{rs[newroot]=tot;ls[newroot]=ls[root];update(rs[root],pos,val,mid+1,r);}return tmp;
} int query(int l,int r,int x,int y,int k)
{int mid=(l+r)/2;if(l==r) return l;int sum=c[ls[y]]-c[ls[x]];if(sum>=k){query(l,mid,ls[x],ls[y],k);}elsequery(mid+1,r,rs[x],rs[y],k-sum);
}
int main(){#ifndef ONLINE_JUDGEfreopen("in.txt","r",stdin);
#endifscanf("%d%d",&n,&q);tot=0;for(int i=1;i<=n;i++)scanf("%d",&a[i]);init();T[n+1]=build(1,m);//cout<<T[0]<<endl;for(int i=1;i<=n;i++){int pos=hash(a[i]);T[i]=update(T[i-1],pos,1,1,m);//cout<<T[i]<<" ";}//cout<<endl;while(q--){int l,r,k;scanf("%d%d%d",&l,&r,&k);printf("%d\n",t[query(1,m,T[l-1],T[r],k)]);} }

版本3

/* ***********************************************
Author        :kuangbin
Created Time  :2013-9-4 20:13:20
File Name     :POJ2104.cpp
************************************************ */#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <math.h>
#include <stdlib.h>
#include <time.h>
using namespace std;const int MAXN = 100010;
const int M = MAXN * 30;
int n,q,m,tot;
int a[MAXN], t[MAXN];
int T[M], lson[M], rson[M], c[M];void Init_hash()
{for(int i = 1; i <= n;i++)t[i] = a[i];sort(t+1,t+1+n);m = unique(t+1,t+1+n)-t-1;
}
int build(int l,int r)
{int root = tot++;c[root] = 0;if(l != r){int mid = (l+r)>>1;lson[root] = build(l,mid);rson[root] = build(mid+1,r);}return root;
}
int hash(int x)
{return lower_bound(t+1,t+1+m,x) - t;
}
int update(int root,int pos,int val)
{int newroot = tot++, tmp = newroot;c[newroot] = c[root] + val;int l = 1, r = m;while(l < r){int mid = (l+r)>>1;if(pos <= mid){lson[newroot] = tot++; rson[newroot] = rson[root];newroot = lson[newroot]; root = lson[root];r = mid;}else{rson[newroot] = tot++; lson[newroot] = lson[root];newroot = rson[newroot]; root = rson[root];l = mid+1;}c[newroot] = c[root] + val;}return tmp;
}
int query(int left_root,int right_root,int k)
{int l = 1, r = m;while( l < r){int mid = (l+r)>>1;if(c[lson[left_root]]-c[lson[right_root]] >= k ){r = mid;left_root = lson[left_root];right_root = lson[right_root];}else{l = mid + 1;k -= c[lson[left_root]] - c[lson[right_root]];left_root = rson[left_root];right_root = rson[right_root];}}return l;
}
int main()
{//freopen("in.txt","r",stdin);//freopen("out.txt","w",stdout);while(scanf("%d%d",&n,&q) == 2){tot = 0;for(int i = 1;i <= n;i++)scanf("%d",&a[i]);Init_hash();T[n+1] = build(1,m);for(int i = n;i ;i--){int pos = hash(a[i]);T[i] = update(T[i+1],pos,1);}while(q--){int l,r,k;scanf("%d%d%d",&l,&r,&k);printf("%d\n",t[query(T[l],T[r+1],k)]);}}return 0;
}

这题的无修改查询的,有修改查询的需要套树状数组,见下一篇

连接:

主席树 | | 可持久化线段树 - L__J

个人对主席树算法的理解

数据结构----主席树相关推荐

  1. 数据结构(主席树):COGS 2211. 谈笑风生

    2211. 谈笑风生 ★★★★   输入文件:laugh.in   输出文件:laugh.out   简单对比 时间限制:3 s   内存限制:512 MB [问题描述] 设T 为一棵有根树,我们做如 ...

  2. 数据结构 - 主席树

    文章目录 好文推荐 求区间第K大 [模板]可持久化线段树 1(主席树) 题目 代码 可持久化数组 [模板]可持久化数组(可持久化线段树/平衡树) 好文推荐 权值线段树.主席树学习 树状结构之主席树 求 ...

  3. 【主席树】可持久化数组(金牌导航 可持久化数据结构-3)

    可持久化数组 金牌导航 可持久化数据结构-3 题目大意 给出一个序列a,让你执行若干操作,操作分为两种: 1.继承第v次操作后把第x个数改成y 2.查询第v次操作的第x个数的值 输入样例 5 10 5 ...

  4. 浅谈数据结构之主席树(线段树进阶版)

    今天看了点主席树的概念,加上飞哥上次讲的,目前对主席树有了大致的了解,简单谈谈吧,不讲代码,只讲思路,日后贴题! Orz高级数据结构发明者主席!!最早在CLJ的课件里第一次看到了这个词,最近做区间第K ...

  5. NOI数据结构:主席树

    主席树详解 主席树详解_西红柿爱炒番茄-CSDN博客 最详细的主席树(不修改,待修改) BZOJ 1901 最详细的主席树(不修改,待修改) BZOJ 1901_Bartholomew_的博客-CSD ...

  6. SPOJ - COT Count on a tree [LCA+主席树]【数据结构】

    题目链接:http://www.spoj.com/problems/COT/en/ -------------------------------------- COT - Count on a tr ...

  7. hdu 2665(主席树查询区间k大值)

    先贴我自己写的代码做模板虽然跟原博主没什么两样.(一开始空间开的4*maxn,交到hdu上一直TLE很奇怪) #include<bits/stdc++.h> using namespace ...

  8. [BZOJ4556][TJOI2016HEOI2016]字符串(二分答案+后缀数组+RMQ+主席树)

    4556: [Tjoi2016&Heoi2016]字符串 Time Limit: 20 Sec  Memory Limit: 128 MB Submit: 1360  Solved: 545 ...

  9. 玲珑杯 1157 - 造物主的戒律 主席树+离散化

    题目链接:http://www.ifrog.cc/acm/problem/1157 1157 - 造物主的戒律 Time Limit:20s Memory Limit:512MByte Submiss ...

最新文章

  1. SD-WAN — 技术架构
  2. 中国煤化工行业运行现状与前景规划动向预测报告2022-2028年
  3. 坚持使用GNU/Linux
  4. epub 机器人系列 阿西莫夫_艾萨克·阿西莫夫是不是穿越来的,他怎么能那么准地预言机器人,互联网技术呢?...
  5. vsftp不允许切换到其它目录_IntelliJ IDEA如何对project的目录进行筛选显示?
  6. C与C++中const差别
  7. iphone保修期多久_秒杀!仅9.9元起换iphone原厂电池!完美解决手机耗电快!
  8. Tensorflow:模型调参
  9. 【3005】拦截导弹问题(noip1999)
  10. D-tale功能的探索
  11. 电力拖动自动控制系统_一、自动化专业电气工程及其自动化
  12. k8s haproxy Unable to connect to the server: x509: certificate is valid for 127.0.0.1, 10.0.1.12
  13. ArcGIS Pro与ArcGis区别
  14. 时代落在英伟达身上的是粒什么沙,国产GPU的机会又在哪?
  15. 采样定理与奈奎斯特极限
  16. 中国自然地理分区数据集 (含农业区划、森林工程、生态保护区、九大流域等)
  17. echarts柱状图改变标签的位置及柱状图颜色
  18. Laravel学习记录--访问器与修改器
  19. ubuntu文件系统字体底纹含义
  20. (二)进程管理之进程状态及组织方式

热门文章

  1. VM是什么,干什么的
  2. 分峰截幅c语言算法,光纤布拉格光栅传感解调中的寻峰算法.docx
  3. Google Bigtable 中文版
  4. 如何把wps随机数据固定_WPS Excel:巧用随机函数rand和randbetween生成各种数据
  5. 创建ITable不能更新记录的问题
  6. 改善用户体念:jsp+Ajax实现实时上传,删除,导入Excel进度条
  7. XUL使用中的常见错误
  8. 波卡Polkadot
  9. 分享一下我作为技能型自由职业者的常用赚钱软件
  10. [146]PyUserInput模拟鼠标和键盘模拟