算法整理 & 复习:Splay



洛谷博客


旋转的规则

一:Zig

此时节点 u 是 root
v 是左孩子:右旋
v 是右孩子:左旋


二:Zig_Zig ( v , u 同侧,先 u 再 v)

此时节点 u 不是 root
v 与 u 同为左孩子: 右旋两次
v 与 u 同为右孩子: 左旋两次


三:Zig_Zag ( v ,u 异侧,先 v 再 u)

此时节点 u 不是 root
v 是左孩子,u 是右孩子:v 先右旋,再左旋
v 是右孩子,u 是左孩子:v 先左旋,再右旋





一、旋转

Splay 的核心操作


struct node{
	int fa,id,cnt,size;
	int son[2]; // 0 为左儿子, 1 为右儿子 
}tree[MAXN];

void updata(int x)
{
    int Ls = tree[x].son[0];
    int Rs = tree[x].son[1];
    tree[x].size = tree[Ls].size + tree[Rs].size + tree[x].cnt;

}
void rotate(int x)
{
	int y = tree[x].fa; 		//fa
	int z = tree[y].fa;			//grandfa
	int k = tree[y].son[1]==x;	//left | right son
	/**这里很巧妙,笔记一下,用双等于代替 if **/
	tree[z].son[tree[z].son[1]==y] = x;
	tree[x].fa = z;
	tree[y].son[k] = tree[x].son[k^1];
	tree[tree[x].son[k^1]].fa = y;
	tree[x].son[k^1] = y;
	tree[y].fa = x;
}
void Splay(int x, int goal) //将 x 旋转为 goal 的儿子
{
	while (tree[x].fa != goal)
	{
		int y = tree[x].fa;
		int z = tree[y].fa;
		if (z != goal)
		{
			(tree[y].son[1]==x) ^ (tree[z].son[1]==y) ? rotate(x) : rotate(y);
		}
		rotate(x);
	}
	if (goal == 0)
	{
		root = x;
	}
}




二、普通平衡树


平衡树的基本操作,包括插入节点、删除节点、查找前驱后继、找 k 大(小)值等等。


P3369 【模板】普通平衡树

// find: 
// 找到 x 所在的位置,转到 root 
/******************************************/
void find(int x)
{
	int u = root;
	if (!u)
    {
        return;
    }
	while (x!=tree[u].id && tree[u].son[x>tree[u].id])
	{
		u = tree[u].son[x>tree[u].id];
	}
	Splay(u, 0);
}
//前驱 & 后继 
// 先 find 
// 前驱为左子树最右点,后继反之
int NEXT(int x, int k)	// k 为 0 时找前驱,为 1 时找后继  
{
	find(x);
	int u = root;
	if ((tree[u].id>x&&k) || (tree[u].id<x&&!k))
	{
		return u;
	}
	u = tree[u].son[k];
	while (tree[u].son[k^1])
	{
		u = tree[u].son[k^1];
	}
	return u;
}
void insert(int x)
{
	int u = root;
	int fa = 0;
	while (tree[u].id!=x && u)
	{
		fa = u;
		u = tree[u].son[x>tree[u].id];
	}
	if (u)
	{
		tree[u].cnt++;
		Splay(u, 0);
	}
	else
	{
		u = ++tot;
		if (fa)
		{
			tree[fa].son[x>tree[fa].id] = u;
		}
		tree[u].id = x;
		tree[u].fa = fa;
		tree[u].son[1] = tree[u].son[0] = 0;
		tree[u].cnt = tree[u].size = 1;
		Splay(tot, 0);
	}
}
void Delete(int x)
{
	int last = NEXT(x, 0);
	int next = NEXT(x, 1);
	Splay(last, 0);
	Splay(next, last);
	int del = tree[next].son[0];
	if (tree[del].cnt > 0)
	{
		tree[del].cnt--;
		Splay(del,0);
	}
	else
	{
		tree[next].son[0] = 0;
	}
}
int kth(int k)
{
	int u = root;
	if (tree[u].size < k)
	{
		return 0;
	}
	while (1)
	{
		int Ls = tree[u].son[0];
		if(tree[Ls].size+tree[u].cnt < k)
		{
			k -= (tree[u].cnt+tree[Ls].size);
			u = tree[u].son[1];
		}
		else if(tree[Ls].size >= k)
		{
			u = Ls;
		}
		else
		{
			return tree[u].id;
		}
	}
}
int Search(int x)	// x 的排名 
{
	find(x);
	return tree[tree[root].son[0]].size;
}

int main(void)	//虽然不知道为什么,但一定要先 insert 两个最值,不然会 T 
{				//这样的话 kth(x+1) 才是答案 
	insert(-0x7fffffff);
	insert( 0x7fffffff);
	return 0;
}



三、文艺平衡树


平衡树的区间反转


P3391 【模板】文艺平衡树

#include <stdio.h>
#include <iostream>
using namespace std;
#define MAXN 100005

struct node{
    int fa, id, tag, size;
    int son[2];
}tree[MAXN];

int root, tot;

void down(int x)
{
    if (tree[x].tag && x)
    {
        tree[tree[x].son[0]].tag ^= 1;
        tree[tree[x].son[1]].tag ^= 1;
        tree[x].tag = 0;
        swap(tree[x].son[0], tree[x].son[1]);
    }
}
void update(int x)
{
    int Ls = tree[x].son[0];
    int Rs = tree[x].son[1];
    tree[x].size = tree[Ls].size + tree[Rs].size + 1;
}
void rotate(int x)
{
    int fa = tree[x].fa;
    int grdf = tree[fa].fa;
    int k = tree[fa].son[1]==x;
    tree[grdf].son[tree[grdf].son[1]==fa] = x;
    tree[x].fa = grdf;
    tree[fa].son[k] = tree[x].son[k^1];
    tree[tree[x].son[k^1]].fa = fa;
    tree[x].son[k^1] = fa;
    tree[fa].fa = x;
    update(fa);
    update(x);
}
void Splay(int x, int goal)
{
    while (tree[x].fa != goal)
    {
        int fa = tree[x].fa;
        int grdf = tree[fa].fa;
        if (grdf != goal)
        {
            ((tree[grdf].son[1] == fa) ^ (tree[fa].son[1] == x)) ? rotate(x) : rotate(fa);
        }
        rotate(x);
    }
    if (goal == 0)
    {
        root = x;
    }
}
void Insert(int x)
{
    int u = root;
    int fa = 0;
    while (u)
    {
        fa = u;
        u = tree[u].son[tree[u].id<x];
    }
    tree[++tot].id = x;
    tree[tot].fa = fa;
    tree[tot].son[0] = tree[tot].son[1] = 0;
    tree[tot].size = 1;
    if (fa)
    {
        tree[fa].son[tree[fa].id<x] = tot;
    }
    Splay(tot, 0);
}
int kth(int k)
{
    int u = root;
    while (1)
    {
        down(u);
        if (tree[tree[u].son[0]].size + 1 < k)
        {
            k -= tree[tree[u].son[0]].size + 1;
            u = tree[u].son[1];
        }
        else if (tree[tree[u].son[0]].size >= k)
        {
            u = tree[u].son[0];
        }
        else
        {
            return u;
        }
    }
}
void Reverse(int l, int r)
{
    int last = kth(l);
    int next = kth(r + 2);
    Splay(last, 0);
    Splay(next, last);
    int pos = tree[next].son[0];
    tree[pos].tag ^= 1;
}
void Print(int u)
{
    down(u);
    if (tree[u].son[0])
    {
        Print(tree[u].son[0]);
    }
    if (tree[u].id != -0x7fffffff && tree[u].id != 0x7fffffff)
    {
        cout << tree[u].id << " ";
    }
    if (tree[u].son[1])
    {
        Print(tree[u].son[1]);
    }
}
int main(void)
{
    Insert(-0x7fffffff);
    Insert( 0x7fffffff);
    int n,m;
    cin >> n >> m;
    for (int i=1;i<=n;i++)
    {
        Insert(i);
    }
    for (int i=1;i<=m;i++)
    {
        int l,r;
        cin >> l >> r;
        Reverse(l, r);
    }
    Print(root);
    return 0;
}





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SP FA

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值