可持久化Treap


終於寫了一次可持久化Treap,做的是可持久化序列的模板題。

Treap

Treap=Tree+Heap,是一個隨機化的數據結構。它的每個節點至少有兩個關鍵字,一個是我們要存儲的\(val\),一個是隨機堆關鍵字,我把它稱為\(hp\)。Treap滿足的性質是\(val\)從小到大,並且每個節點的\(hp\)都小於(或都大於)兒子節點的\(hp\)值。也就是說,通過一個隨機數來讓Treap具有堆的性質,從而使得其期望深度為\(O(logn)\)

旋轉

Treap可以通過旋轉來保持其平衡,操作與splay類似。

非旋轉

非旋轉Treap是本文的重點。由於Treap同時具有二叉搜索樹和堆的性質,我們考慮利用堆的性質來保持平衡。想一想之前提到過的左偏樹的平衡方法,我們可以得到一個基於SplitMerge操作的Treap,稱為非旋轉Treap。

Split

\(split(x,k)\)返回一個\(pair\),表示把\(x\)為根的樹的前\(k\)個元素放在一顆樹中,后面的放在另一顆樹中,返回這兩棵樹的根。

這個操作實現起來非常簡單。如果\(x\)的左子樹的\(size\ge k\),那么直接遞歸進左子樹,把左子樹分出來的第二顆樹和當前的\(x\)和右子樹合並。否則遞歸右子樹。寫的時候注意一下順序即可。

Merge

\(merge(x,y)\)返回merge出的樹的根。同樣遞歸實現。如果\(hp(x)<hp(y)\),則\(merge(rc(x),y)\),否則\(merge(x,lc(y))\)

非旋轉Treap的關鍵在於不需要維護父親節點的信息,故可以可持久化!

每次split和merge走到的所有點都新建一個即可。注意下傳標記也要新建點。

代碼

可持久化序列這道題要求支持三個操作:

  • \(\text{1 l r}\),翻轉\(l\)\(r\)的區間
  • \(\text{2 l r}\),詢問\(l\)的到\(r\)的區間和
  • \(\text{3 p}\),回到\(p\)時刻

每次修改新建點打翻轉標記即可。

#include<cstdio>
#include<cctype>
#include<cstring>
#include<cstdlib>
#include<ctime>
#include<utility>
#include<algorithm>
using namespace std;
typedef pair<int,int> Pair;
int read() {
	int x=0,f=1;
	char c=getchar();
	for (;!isdigit(c);c=getchar()) if (c=='-') f=-1;
	for (;isdigit(c);c=getchar()) x=x*10+c-'0';
	return x*f;
}
const int maxn=5e4+5;
const int nlogn=1.3e7+5;
struct node {
	int x,hp,l,r,sum,size;
	bool rev;
	void clear() {
		x=hp=l=r=sum=size=rev=0;
	}
};
struct TREAP {
	int pool[nlogn];
	int pooler;
	node t[nlogn];
	int now,all;
	int root[maxn];
	TREAP ():now(0),pooler(1) {
		for (int i=1;i<nlogn;++i) pool[i]=i;
		root[now]=pool[pooler++];
	}
	int newroot() {
		int ret=pool[pooler++];
		return ret;
	}
	int newnode(int x) {
		int ret=pool[pooler++];
		t[ret].hp=rand();
		t[ret].size=1;
		t[ret].x=t[ret].sum=x;
		return ret;
	}
	void delnode(int x) {
		t[x].clear();
		pool[--pooler]=x;
	}
	void next() {
		root[++all]=newroot();
		t[root[all]]=t[root[now]];
		now=all;
	}
	void back(int x) {
		now=x;
	}
	void update(int x) {
		t[x].sum=t[x].x+t[t[x].l].sum+t[t[x].r].sum;
		t[x].size=t[t[x].l].size+t[t[x].r].size+1;
	}
	void pushdown(int x) {
		if (!t[x].rev) return;
		if (t[x].l) {
			int tx=newnode(t[t[x].l].x);
			t[tx]=t[t[x].l];
			t[tx].rev^=true;
			t[x].l=tx;
		}
		if (t[x].r) {
			int tx=newnode(t[t[x].r].x);
			t[tx]=t[t[x].r];
			t[tx].rev^=true;
			t[x].r=tx;
		}
		swap(t[x].l,t[x].r);
		t[x].rev=false;
	}
	int merge(int x,int y) {
		if (!x) return y;
		if (!y) return x;
		int now;
		if (t[x].hp<=t[y].hp) {
			now=newnode(t[x].x);
			t[now]=t[x];
			pushdown(now);
			t[now].r=merge(t[now].r,y);
		} else {
			now=newnode(t[y].x);
			t[now]=t[y];
			pushdown(now);
			t[now].l=merge(x,t[now].l);
		}
		update(now);
		return now;
	}
	Pair split(int x,int p) {
		if (t[x].size==p) return make_pair(x,0);
		int now=newnode(t[x].x);
		t[now]=t[x];
		pushdown(now);
		int l=t[now].l,r=t[now].r;
		if (t[l].size>=p) {
			t[now].l=0;
			update(now);
			Pair g=split(l,p);
			now=merge(g.second,now);
			return make_pair(g.first,now);
		} else if (t[l].size+1==p) {
			t[now].r=0;
			update(now);
			return make_pair(now,r);
		} else {
			t[now].r=0;
			update(now);
			Pair g=split(r,p-t[l].size-1);
			now=merge(now,g.first);
			return make_pair(now,g.second);
		}
	}
	void rever(int l,int r) {
		++l,++r;
		Pair g=split(root[now],l-1);
		Pair h=split(g.second,r-l+1);
		int want=h.first;
		int here=newnode(t[want].x);
		t[here]=t[want];
		t[here].rev^=true;
		int fi=merge(g.first,here);
		int se=merge(fi,h.second);
		root[now]=se;
	}
	int query(int l,int r) {
		++l,++r;
		Pair g=split(root[now],l-1);
		Pair h=split(g.second,r-l+1);
		int want=h.first;
		int ret=t[want].sum;
		int fi=merge(g.first,want);
		int se=merge(fi,h.second);
		root[now]=se;
		return ret;
	}
	void insert(int x) {
		int k=newnode(x);
		root[now]=merge(root[now],k);
	}
} Treap;
int main() {
#ifndef ONLINE_JUDGE
	freopen("test.in","r",stdin);
	freopen("my.out","w",stdout);
#endif
	srand(time(0));
	int n=read(),m=read();
	for (int i=1;i<=n;++i) {
		int x=read();
		Treap.insert(x);
	} 
	while (m--) {
		int op=read();
		if (op==1) {
			Treap.next();
			int l=read(),r=read();
			Treap.rever(l,r);
		} else if (op==2) {
			int l=read(),r=read();
			int ans=Treap.query(l,r);
			printf("%d\n",ans);
		} else if (op==3) {
			Treap.back(read());
		}
	}
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM