主席树Ⅰ

传说中的“高级”数据结构?

第一篇博客写一道模板题就行了。

主席树和普通的线段树写法上有什么区别?

  • 左右孩子的表示方法

其实这和动态开点的线段树一个性质。我的写法就是把 \(change\) 函数改为 \(int\) 类型,实时返回孩子的编号。

  • 公用信息

主席树的基本思想就是公用信息,能用之前的信息就用,达到节省时间和空间的目的。

所以需要做的就是在 change 函数里增加之前的节点 x ,先开一个新节点,把x里的信息全部复制到新节点里,看需要更新的在左儿子就更新左儿子编号,反之亦然。

  • 记录根节点编号

很好理解。你总得知道某个前缀应该从哪里开始访问吧。

  • 节点数量

为了保险,开区间大小的几十倍差不多。一般直接struct node{...}t[N<<5]即可。

  • 区间访问

不用把两个前缀的信息都求出来再相减,直接边递归边减即可。

#include<cstdio>
#include<algorithm>
//#define zczc
#define error printf("working\n");
using namespace std;
const int N=200010;
inline void read(int &wh){
    wh=0;int f=1;char w=getchar();
    while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
    while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar(); }
    wh*=f;return;
}

int m,n,q,a[N],b[N],c[N];

#define lc t[wh].left
#define rc t[wh].right
#define mid (t[wh].l+t[wh].r>>1)
int nodecnt=0;
struct node{
	int l,r,left,right,data;
}t[N*50];
inline void pushup(int wh){
	t[wh].data=t[lc].data+t[rc].data;
	return;
}
int build(int l,int r){
    int wh=++nodecnt;
	t[wh].l=l,t[wh].r=r;
	if(l==r)return wh;
	lc=build(l,mid);
	rc=build(mid+1,r);
	return wh;
}
int change(int x,int pl){
	int wh=++nodecnt;
	t[wh].l=t[x].l,t[wh].r=t[x].r;
	lc=t[x].left,rc=t[x].right;
	t[wh].data=t[x].data;
	if(t[wh].l==t[wh].r){
		t[wh].data++;
		return wh;
	}
	if(pl<=mid)lc=change(lc,pl);
	else rc=change(rc,pl);
	pushup(wh);
	return wh;
}
int work(int lwh,int rwh,int want,int now){
	if(t[lwh].l==t[lwh].r)return t[lwh].l;
	int ldata=t[t[rwh].left].data-t[t[lwh].left].data;
	if(ldata+now>=want)return work(t[lwh].left,t[rwh].left,want,now);
	else return work(t[lwh].right,t[rwh].right,want,now+ldata);
}
#undef lc
#undef rc
#undef mid 

int root[N];

signed main(){
	
	#ifdef zczc
	freopen("in.txt","r",stdin);
	#endif
	
	read(m);read(q);
	for(int i=1;i<=m;i++){
		read(a[i]);
		b[i]=a[i];
	}
	sort(b+1,b+m+1);
	n=unique(b+1,b+m+1)-b-1;
	for(int i=1;i<=m;i++){
		a[i]=lower_bound(b+1,b+n+1,a[i])-b;
	}
	
    root[0]=build(1,n);
    for(int i=1;i<=m;i++){
    	root[i]=change(root[i-1],a[i]);
	}
	
	int s1,s2,s3;
	while(q--){
		read(s1);read(s2);read(s3);
		printf("%d\n",b[work(root[s1-1],root[s2],s3,0)]);
	}
	
	return 0;
}
posted @ 2021-08-05 21:35  Feyn618  阅读(35)  评论(0编辑  收藏  举报