Application of splay in the interval in question

(I ignorant, if imperfect also please let me know)

1. From the binary search tree talk

First, binary search tree is a binary tree satisfying the following properties

  1. Each binary tree node has a weight value \ (V \)

  2. For each node binary search tree, if it has left subtree, then the weights of each node of its left subtree are less than the node weight values ​​itself

  3. For each node in a binary search tree, if it is a right subtree, then the weights for each node of its right subtree of this node is greater than the value of their own weight

The figure is a binary search tree (thanks EternalAlexander of OI Painter)

FIG 5 is a weight of junction of the left subtree are 1,2,3,4 weight, less than 5
right subtree with a weight of 6,7, 5 were greater than
for the other nodes also meet this beautiful nature

In order to deepen the understanding of everyone binary search tree, we look at a topic:

Maintenance period without repeated sequence elements, supports the following two operations:
. 1 \ (the Insert (X) \) Insert Element \ (X \)
2 $ the Get (X) $ outputs \ (X \) small numbers

For Action 1, we need to find \ (x \) proper insertion position, suppose we now want to (x \) \ inserted to \ (i \) is the root of the subtree (including itself), then there will be two cases:

1. \ (X <Tree [I] .Value \) , described \ (X \) position \ (I \) left subtree
2. \ (X> Tree [I] .Value \) , described \ (X \) position \ (I \) a right subtree

Such access is inserted until the empty node from the root node can be inserted recursively

Well, now we have to solve the operation 1, operation 2 how to do it?

For each node, we maintain a \ (size \) , represented by \ (I \) is the root of the subtree (including \ (I \) ) size

Suppose now that we are looking at \ (I \) of the subtree rooted at \ (K \) a small value, easy to know subtree tree than \ (I \) setting small \ (size [tree [i] .leftson] \) months, so

  1. If \ (K <= size [Tree [I] .leftson] \) , the recursive lookup into the left subtree
  2. If \ (K = size [Tree [I] .leftson] + 1'd \) , then the \ (I \) junction node we are looking
  3. If not, then recursively into the right subtree search, this time \ (k \) should subtract \ (size [tree [i] .leftson] +1 \)

However, the problem of an ordinary binary search tree can be resolved very limited when it comes to the delete operation has become very hard to accept, the tree degenerates into a bad situation chains may also occur (imagine inserting a increasing number) worst complexity of a single operation may reach \ (O (n) \)

The so-called balanced tree, in fact, the original form of the tree may try to balance the imbalance, by \ (rotate \) operations such as the depth of the tree to reach excellent \ (the n-log \) , and \ (splay \) belongs balanced tree in one

Students also will not the weight of splay please look at the explanation of the dalao ~

2. The nature of the basic interval Splay

First, binary search tree before we analogies and weights \ (splay \) character, can be constructed as a tree

  1. For each node in the tree, if it has left subtree, then the position of each node on its left subtree in the original sequence than the forward node

  2. For each node in the tree, if it is a right subtree, then the position of each node on its right subtree in the original sequence than this node on the list

For example, we \ ({1,5,4,2,3} \) establishing interval \ (The splay \)

1. Insert 1

2. Insert 5, since the position 2 on the ratio of 5, 5 thus inserted into the right son of the 2

3. In order to maintain the balance of the tree, we will be 5 \ (splay \) to the root

4. Insert the 4,4 position after 5

5. Similarly, the. 4 \ (The splay \) to the root

最后这棵树可能是这样的(为了好看我就先提前\(splay\)了)

现在我们会发现一个重要的性质:对这棵splay进行中序遍历后得到的恰好是原序列

那么,如果我们把元素在原序列中的位置当做权值,区间splay其实就是一棵每个点的权值都可能动态变化的权值splay,至于为什么权值会变化,是因为这个数在序列中的位置可能发生了变化(比如翻转)

区间splay中,每个结点在储存它本身的信息外,还储存着它子树这段连续区间的信息,比如说上图中标号为1的结点可能除了存自己的value,size以外还存着1,5的value之和,4结点可能存着所有结点value的总和,通过这种信息合并可以像线段树一般大大缩短查询的复杂度

你可以这样认为:区间splay中每个结点代表的不只是它本身,还可以代表它的整颗子树

下面开始讲解一些具体操作:

Kth

区间splay中查找目前序列中第\(k\)位置上的数和二叉搜索树查找第\(k\)小是一摸一样的,代码如下:

int Kth(int x)
{
    int u=root;
    while(1)
    {
        if(t[u].tag)pushdown(u);//这东西之后解释
        if(x<=t[t[u].ch[0]].siz)u=t[u].ch[0];//ch[0],ch[1]分别是左儿子和右儿子,t[x].siz表示以x为根的子树的大小
        else if(t[t[u].ch[0]].siz+1==x)return u;
        else x-=(t[t[u].ch[0]].siz+1),u=t[u].ch[1];
    }
}

Pushup

将子树信息向上合并的函数,具体代码如下(以维护子树和 sum为例)

inline void pushup(int x)
{
    int l=t[x].ch[0],r=t[x].ch[1];
    t[x].sum=t[l].sum+t[r].sum+t[x].v;//一个结点子树之和sum=左子树sum+右子树sum+本身的value
    t[x].siz=t[l].siz+t[r].siz+1;
}

Split

就像LCT中的Access操作,区间splay的核心操作split就是提取一段区间\([l,r]\),具体操作就是先将\(Kth(l-1)\)旋转到根,再把\(Kth(r+1)\)旋转到根的右儿子,那么由于区间splay的中序遍历为原序列,现在\(Kth(r+1)\)的左子树就正好是\([l,r]\)这段区间
代码如下:

int split(int l,int r)
{
    int x=Kth(l-1),y=Kth(r+1);
    splay(x,0),splay(y,x);
    return t[y].ch[0];
}//这样返回的就是代表一段区间[l,r]的结点编号

举个例子,假设还是刚才的\({1,5,4,2,3}\)这段序列,我们现在想要区间\([3,4]\)的和
1 把Kth(3-1)即5旋转到根

2 把Kth(4+1)即3旋转到根的右儿子

可以在上图中很明显的看出区间\([3,4]\)\(4,2\)这两个数就是现在3的左子树,既不少也不多,由于我们在每个结点存了子树和,所以3的左儿子的子树和\(sum\)就是我们要求的区间和

Insert

假设我们要将数k插入到第x个数后面,那么先\(split(Kth(x),Kth(x+1))\)后将x作为现在\(Kth(x+1)\)的左儿子就可以啦

Delete

\(Insert\)操作一样,只不过是将\(Kth(x+2)\)的左儿子删去

Pushdown

现在考虑如何区间修改?
首先,如果我们要将区间\([l,r]\)每个数加上\(k\),那么肯定先要\(split(l,r)\),将这段区间提取出来,之后怎么办?

打懒惰标记?那不是线段树吗?

答案是可以的,由于我们统计答案是自上而下的,所以可以在所有复合操作所共有的\(Kth\)操作中边访问下传标记,这样就能保证解的正确性

代码:(以区间统一赋值为k的标记为例)

inline void pushdown(int x)
{
    int l=t[x].ch[0],r=t[x].ch[1];
    if(t[x].tag)//是否有区间统一赋值的标记
    {
        t[x].tag=0;
        if(l)t[l].tag=1,t[l].v=t[x].v,t[l].sum=t[x].v*t[l].siz;//sum是维护的子树和
        if(r)t[r].tag=1,t[r].v=t[x].v,t[r].sum=t[x].v*t[r].siz;
    }
}

其实你会发现区间splay对于区间加,区间乘,区间赋值,区间Rmq问题的处理和线段树是一模一样的,大多数线段树都可以用区间splay代替,而区间splay能处理的问题会更多

区间splay的\(rotate,splay\)和权值splay并没有什么区别,这里不再赘述

下面看一道例题:

您需要写一种数据结构,来维护一个数列,其中需要提供以下操作:翻转一个区间,例如原序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1

首先构建区间splay,不难发现翻转一段区间\([l,r]\)就是将代表这个区间的结点的子树中每一对左右子树都交换

区间操作肯定会想到打标记啦,只要用一个tag表示区间翻转的懒惰标记就可以了,下传标记就是左右子树的标记^=1,然后交换左右子树就好了

细节:这样如果修改区间\([1,n]\)会出问题,所以加入两个哨兵结点\(1,n+2\)防止越界,那么我们的序列下标都+1就好了

这里是丑陋的代码~

const int maxn=100000+10;
struct node
{
    int ff,val;//父亲,权值
    int ch[2];//左右儿子
    int siz;//子树大小
    int tag;//翻转的懒惰标记
}t[maxn];
int tot,n,root,m;
inline void pushup(int x)//信息向上合并
{
    t[x].siz=t[t[x].ch[0]].siz+t[t[x].ch[1]].siz+1;
}
inline void pushdown(int x)//下传标记
{
    t[t[x].ch[0]].tag^=1,t[t[x].ch[1]].tag^=1;
    swap(t[x].ch[0],t[x].ch[1]);
    t[x].tag=0;
}
inline void rotate(int x)
{
    int y=t[x].ff,z=t[y].ff;
    int k=(t[y].ch[1]==x);
    t[z].ch[t[z].ch[1]==y]=x,t[x].ff=z;
    t[y].ch[k]=t[x].ch[k^1],t[t[x].ch[k^1]].ff=y;
    t[x].ch[k^1]=y,t[y].ff=x;
    pushup(y),pushup(x);
}
void splay(int x,int goal)
{
    while(t[x].ff!=goal)
    {
        int y=t[x].ff,z=t[y].ff;
        if(z!=goal)
            (t[y].ch[0]==x)^(t[z].ch[0]==y)?rotate(x):rotate(y);
        rotate(x);
    }
    if(goal==0)root=x;
}
void insert(int x)//插入第x个数
{
    int u=root,ff=0;
    while(u)ff=u,u=t[u].ch[x>t[u].val];
    u=++tot;
    if(ff)t[ff].ch[x>t[ff].val]=x;
    t[u].val=x,t[u].siz=1,t[u].ff=ff;
    t[u].tag=0,t[u].ch[0]=t[u].ch[1]=0;
    splay(u,0);
}
int Kth(int x)//查询序列中第x个数在splay中的位置
{
    int u=root;
    while(1)
    {
        if(t[u].tag)pushdown(u);
        if(x<=t[t[u].ch[0]].siz)u=t[u].ch[0];
        else if(t[t[u].ch[0]].siz+1==x)return u;
        else x-=(t[t[u].ch[0]].siz+1),u=t[u].ch[1];
    }
}
void solve(int l,int r)
{
    l=Kth(l),r=Kth(r+2);//相当于split(l,r),由于有哨兵结点所以下标+1
    splay(l,0),splay(r,l);
    t[t[r].ch[0]].tag^=1;
}
void print(int x)
{
    if(!x)return;
    if(t[x].tag)pushdown(x);
    print(t[x].ch[0]);
    if(t[x].val>1&&t[x].val<n+2)printf("%d ",t[x].val-1);
    print(t[x].ch[1]);
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n+2;++i)insert(i);
    int l,r;
    for(int i=1;i<=m;++i)scanf("%d%d",&l,&r),solve(l,r);
    print(root);
}

3.一些习题

  1. P3391 【模板】文艺平衡树(Splay) 模板题QwQ
  2. SPOJ4487 GSS6带插入区间最大子段和,同线段树一样 \(pushup\) 区间信息即可
  3. P2042 [NOI2005]维护数列 操作比较多,且涉及到 \(splay\) 的线性构造

Guess you like

Origin www.cnblogs.com/study-ysj/p/splay.html