BZOJ 1251 序列终结者 平衡树 无旋treap

因为感觉 S p l a y Splay Splay比较难学。所以尝试去学了一下功能也同样强大的无旋 T r e a p Treap Treap,但是并没有理解很透彻。
无旋 T r e a p Treap Treap主要只有两个操作:
1. s p l i t : 1.split: 1.split: 把当前的树分割为两个平衡树。假设当前要取出前一个树的大小为 k k k,比较左子树的大小和 k k k,假如 k ≤ s z [ l s [ r t ] ] k\leq sz[ls[rt]] ksz[ls[rt]],那么当前根一定是右子树的根,然后递归左儿子。左儿子的第一个根是左边的根,第二个根是右子树根的左儿子。对于另外一种情形,类似的讨论。
2. m e r g e : 2.merge: 2.merge合并两个平衡树。而且总是把权值小的那个根作为最终的根。这样合并的方式就一定是唯一的。假设有两个树 r t 1 , r t 2 rt1,rt2 rt1,rt2,并且第一个根的权值小于第二个根。就递归把第一个根的右儿子和第二个根合并。
这个题的题意是开始给出一个全部为0的序列。
然后支持三种操作:区间加,区间最值和区间翻转。
这里用 T r e a p Treap Treap方便实现区间翻转,直接分裂两次取出 [ l , r ] [l,r] [l,r]这一段,然后在根上打上标记,同时交换左右两个儿子。对于区间加也用同样的标记的方法。区间最值在 p u s h u p pushup pushup的过程中更新。

#include
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const ll INF=LONG_LONG_MAX;
const int N=5e4+7; 
int n,m,x,y,z,rt;
int r[N],sz[N],ls[N],rs[N];
ll v[N],mx[N];
int tag1[N],tag2[N];
int tot=0; 
int build(int rt) {
	r[++tot]=rand();
	sz[tot]=1;
	return tot;
}
void add(int rt,int val) {
	mx[rt]+=val;
	tag2[rt]+=val;
	v[rt]+=val;
}
void rotate(int rt) {
	swap(ls[rt],rs[rt]);
	tag1[rt]^=1;
}
void pushup(int rt) {
	sz[rt]=sz[ls[rt]]+sz[rs[rt]]+1;
	mx[rt]=v[rt];
	if(ls[rt]) mx[rt]=max(mx[rt],mx[ls[rt]]);
	if(rs[rt]) mx[rt]=max(mx[rt],mx[rs[rt]]);
}
void pushdown(int rt) {
	if(tag1[rt]) {
		if(ls[rt]) rotate(ls[rt]);
		if(rs[rt]) rotate(rs[rt]);
		tag1[rt]=0;
	}
	if(tag2[rt]) {
		if(ls[rt]) add(ls[rt],tag2[rt]);
		if(rs[rt]) add(rs[rt],tag2[rt]);
		tag2[rt]=0;
	}
}
void split(int rt,int k,int &x,int &y) {
	if(!rt) { x=y=0;return; }
	pushdown(rt);
	if(sz[ls[rt]]

你可能感兴趣的:(比赛题解)