首页 > 学院 > 开发设计 > 正文

浅谈主席树

2019-11-08 18:38:16
字体:
来源:转载
供稿:网友

浅谈主席树思想以及模板


浅谈主席树思想以及模板主席树的原理主席树的思想主席树的应用主席树的模板主席树的题目

首先,作为一名蒟蒻,很早就听说过主席树这个高大上的名字,然而一直都不会(其实只是没敲过代码)。所以,今天就来正式的谈一谈主席树,本蒻著此文以记之

主席树的原理

它的原理十分简单,就是想办法把前后两棵线段树进行持久化并尽可能合并以节约空间。最后的空间复杂度大概是O(nlogn) 这里写图片描述

然而这个时候它就会很自然地要求我们保证所有线段树的结构均完全相同,这时我们可以通过建立一个参考序列,里面保存着区间,映射到主席树里就是保存和这一区间有关的信息,然后对这个参考区间进行二分,这样映射到每棵线段树中它们的结构都将会是完全相同的

主席树的思想

就我个人的理解,主席树的本质,其实就是一颗可持久化线段树。但是本来一颗好好的线段树,我们为什么要把他们造成可持久化的呢,这是因为我们想访问多个区间或多个子线段树。此处的意思是我们建立一颗普通的线段树时,我们只是使这棵线段树对应了一个区间[1...n],但往往我们并不想仅仅去访问这一棵线段树上的信息或者说这一段区间上的信(这可能是由于一棵线段树上的信息量太少而不能满足我们的需要)。有时我们可以直接使用原有的那棵线段树上的信息(比如我们想查询一段子区间的Maximum/Minimum或者是Sum什么的),然而有的时候就并不可以(如我们想求一段子区间中的第K大值)。对于什么时候Single-Segment-Tree会出现问题应该根据具体情况就可以判断出来

主席树的应用

作为一名蒟蒻,我做的题非常少,对于这种神奇的数据结构,应用可能有求某段区间中第K大值或者是求某段区间中某个或某段数字的出现次数等等,此处留坑待补

主席树的模板

下面的这个模板的功能是求某段区间第K小的值(当然是用主席树实现的),代码巨丑,不喜勿喷

#include<cstdlib>#include<cstdio>#include<algorithm>#define maxn 100005#define Pii pair<int,int>#define Author Goseqhusing namespace std;typedef struct node{ int sum; node* left; node* right; node(int sum){ this->sum=sum; right=left=NULL; } node():sum(0),left(NULL),right(NULL){}}node;//主席树的结点node* list[maxn];node* op;node* op2;Pii line[maxn];Pii sline[maxn];//排序之后的line数组(参考区间)int n,m,a,b,c;void insert(int l,int r,int o,int i){ if(l==r) return; int m=(r-l)/2+l; if(line[i]>sline[m]){ op->left=op2->left; node* k=new node(op2->right->sum+1); op->right=k,op=k,op2=op2->right; insert(m+1,r,o*2+1,i); } else{ op->right=op2->right; node* k=new node(op2->left->sum+1); op->left=k,op=k,op2=op2->left; insert(l,m,o*2,i); } return;}//主席树的二分插值过程/*void init2(int l,int r,int x,node* now){ if(l==r) return; int m=(r-l)/2+l; if(x>sline[m]){ node* now->left=new node(0); node* now->right=new node(1); } else{ node* now->left=new node(1); node* now->right=new node(0); } init2(l,m,x,now->left); init2(m+1,r,x,now->right); return;}*///此处对应了另一种实现,即以第一棵有意义的线段树作为开头void init2(int l,int r,node* now){ if(l==r)return; int m=(r-l)/2+l; now->left=new node(); now->right=new node(); init2(l,m,now->left); init2(m+1,r,now->right); return;}//这里是用一棵虚拟的线段树作为开头,感觉更加简洁不易出错void init(){ //init2(1,n,line[1],list[1]); list[0]=new node(); init2(1,n,list[0]); for(int i=1;i<=n;i++){ op=list[i]=new node(),op2=list[i-1]; insert(1,n,1,i); } return;}int query(int l,int r){ if(l==r) return sline[l].first; int con=op2->left->sum-op->left->sum; int m=(r-l)/2+l; if(c>con){ c-=con; op=op->right,op2=op2->right; return query(m+1,r); } else{ op=op->left,op2=op2->left; return query(l,m); }}//询问过程int main(){ /*freopen("input.txt","r",stdin); freopen("output.txt","w",stdout);*/ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++){ scanf("%d",&line[i].first); line[i].second=i; sline[i]=line[i]; } sort(sline+1,sline+n+1); init(); for(int i=0;i<m;i++){ scanf("%d%d%d",&a,&b,&c); op=list[a-1],op2=list[b]; PRintf("%d/n",query(1,n)); } return 0;}

主席树的题目

这就等着其他的博文来进行补充啦O(∩_∩)O~


上一篇:sscanf函数

下一篇:1004. Counting Leaves (30)

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表