Splay

Splay 模板(国庆作业表)

void update(ll x){sz[x] = sz[son[x][1]] + sz[son[x][0]] + cnt[x];}
// cnt[x] 表示将dt相等的值的个数总和
ll son_type(ll x){return x == son[fa[x]][1];}
void rotate(ll x){
	ll y = fa[x], z = fa[y],k = son_type(x);
	
	if(z){son[z][son_type(y)] = x;}
	else root = x;fa[x] = z;
	
	son[y][k] = son[x][k^1];
	fa[son[y][k]] = y;
	
	son[x][k^1] = y;fa[y] = x;
	update(y);update(x);
}
//将x号节点,移到p号点的下面
void splay(ll x,ll p){
	for(ll f = fa[x];f = fa[x],f != p;rotate(x))
	if(fa[f])rotate(son_type(f)==son_type(x)?f:x);
	if(!p)root = x;
}
void insert(ll x){
	if(!root){
		root = ++tot;
		fa[tot] = 0;
		sz[tot] = 1;
		cnt[tot] = 1;
		dt[tot] = x;
		return;
	}
	int pos = getsuc(root,x);
	if(dt[pos] == x){splay(pos,0);sz[pos]++;cnt[pos]++;return;}
	++tot;ll p = root;
	while(!fa[tot])
	{
		sz[p]++;
		if(x<dt[p])
			if(son[p][0])p = son[p][0];else son[p][0] = tot,fa[tot] = p;
		else if(son[p][1])p = son[p][1];else son[p][1] = tot,fa[tot] = p;
	}
	dt[tot] = x;cnt[tot] = sz[tot] = 1;
	splay(tot,0);
}

求rank操作(+翻转)

void pushdown(int p){
	int &ls = son[p][0],&rs = son[p][1];
	lzy[ls]^=1;lzy[rs]^=1;
	swap(ls,rs);//翻转就是将一个节点的左右儿子交换位置
	lzy[p] = 0;
}
int find(int k){
	int p=root;
	while(p)
	{
		if(lzy[p])pushdown(p);//此为翻转操作
		if(k<=sz[son[p][1]])p=son[p][1];
		else if(sz[son[p][1]]+cnt[p]>=k)break;
		else{
			k-=sz[son[p][1]]+cnt[p];
			p=son[p][0];
		}
	}
	splay(p,0);
	return p;
}

求后继(前趋同理)

ll getsuc(ll p,ll x){
	ll res = 0,ls = son[p][0],rs = son[p][1];
	if(x<dt[p]&&ls)res = getsuc(ls,x);
	if(res)return res;
	if(x<=dt[p])return res = p;
	if(rs)res = getsuc(rs,x);
	return res;
}

删除操作

void Del(int key)
{
	int p=Find(key); //找到要被删除的节点
	if(p){
		Splay(p); //将待删节点旋转到根
		int Ls=Lson[p],Rs=Rson[p];
		Father[Ls]=Lson[p]=Rson[p]=Father[Rs]=0; //删除
		if(Ls==0){ root=Rs; Father[Rs]=0;} //若p没有左儿子,则右儿子当新的根节点
		else{//找左子树中值最大者当新的根节点
			root=Ls;
			Ls=GetMax(Ls);
			Splay(Ls);
			Father[Ls]=0;
			Father[Rs]=Ls;
			Rson[Ls]=Rs;
			Size[root]+=Size[Rson[root]]; //注意:别忘了更新根节点的size值
		}
	}
}
上一篇:题解「JOI Open 2021」决算报告


下一篇:「笔记」后缀自动机 new