洛谷P3391 文艺平衡树---Splay

题目链接:https://www.luogu.com.cn/problem/P3391

时间限制1.00s
内存限制125.00MB
 

题目描述

您需要写一种数据结构(可参考题目标题),来维护一个有序数列。

其中需要提供以下操作:翻转一个区间,例如原有序序列是 5 4 3 2 1,翻转区间是 [2,4] 的话,结果是 5 2 3 4 1。

输入格式

第一行两个正整数 n,m表示序列长度与操作个数。序列中第 i 项初始为 i
接下来 m 行,每行两个正整数 l,r,表示翻转的区间。

输出格式

输出一行 n 个正整数,表示原始序列经过 m 次变换后的结果。

输入输出样例

输入
5 3
1 3
1 3
1 4
输出 
4 3 2 1 5

说明/提示

【数据范围】
对于 100% 的数据,1n,m100000,1lrn。


 

emmm,没什么好说的,上来就是个Splay板子,然后想想,如果按照它原来的顺序建树的话就是一条只有右儿子的链。我们考虑使用线段树的方式,一直找中点,就可以使得它的深度接近完美:

int build(int l,int r,int fa)
{
    if (l>r) return 0;
    int mid=(l+r)>>1;
    int rt=++num;
    tree[rt].f=fa;
    tree[rt].son[0]=tree[rt].son[1]=0;
    tree[rt].cnt++;
    tree[rt].val=a[mid];
    tree[rt].size++;
    tree[rt].son[0]=build(l,mid-1,rt);
    tree[rt].son[1]=build(mid+1,r,rt);
    update(rt);
    return rt;
}

然后就开始翻转了。。。实际上翻转操作的话我们只需要将l-1伸展到根结点,r+1伸展到根结点的右儿子,那么剩下的就是区间[l,r]了:

洛谷P3391 文艺平衡树---Splay_第1张图片

 

我们找到l~r的第一结点打上标记后下传,对于有标记的我们直接翻转左右儿子就达到翻转的效果了。至于l~r的第一结点位置就很好找,它是根结点的右儿子的左儿子。

于是我们的翻转操作就可以完美写出来了:

void reverse(int l,int r)
{
    l--,r++;
    l=find(l);r=find(r);
    splay(l,0);//将l-1伸展到根节点
    splay(r,l);//将r+1伸展到根节点后继
    int pos=tree[root].son[1];
    pos=tree[pos].son[0];//找到l-r所在的位置
    tree[pos].tag^=1;//打旋转标记
}

然后套个splay的板子,find(x)是指找到x所在树中的位置,类似于权值线段树的写法:

int find(int x)//找到x所在的结点
{
    int rt=root;
    while (1){
        push_down(rt);
        if (x<=tree[tree[rt].son[0]].size) rt=tree[rt].son[0];
        else {
            x-=tree[tree[rt].son[0]].size+1;
            if (!x) return rt;
            rt=tree[rt].son[1];
        }
    }
}

以下是AC代码:

#include 
#include 
#include 
using namespace std;

const int mac=1e5+10;

struct Tree
{
    int son[2],cnt,val,f,size,tag;
}tree[mac];
int root,num,a[mac],_min,_max;

void update(int rt)
{
    if (!rt) return;
    tree[rt].size=tree[rt].cnt;
    if (tree[rt].son[0]) tree[rt].size+=tree[tree[rt].son[0]].size;
    if (tree[rt].son[1]) tree[rt].size+=tree[tree[rt].son[1]].size;
}

int build(int l,int r,int fa)
{
    if (l>r) return 0;
    int mid=(l+r)>>1;
    int rt=++num;
    tree[rt].f=fa;
    tree[rt].son[0]=tree[rt].son[1]=0;
    tree[rt].cnt++;
    tree[rt].val=a[mid];
    tree[rt].size++;
    tree[rt].son[0]=build(l,mid-1,rt);
    tree[rt].son[1]=build(mid+1,r,rt);
    update(rt);
    return rt;
}

void push_down(int rt)//传下旋转标记
{
    if ((!rt) || (!tree[rt].tag)) return;
    tree[tree[rt].son[0]].tag^=1;
    tree[tree[rt].son[1]].tag^=1;
    swap(tree[rt].son[0],tree[rt].son[1]);
    tree[rt].tag=0;
}

int find(int x)//找到x所在的结点
{
    int rt=root;
    while (1){
        push_down(rt);
        if (x<=tree[tree[rt].son[0]].size) rt=tree[rt].son[0];
        else {
            x-=tree[tree[rt].son[0]].size+1;
            if (!x) return rt;
            rt=tree[rt].son[1];
        }
    }
}

bool which(int x){return x==tree[tree[x].f].son[1];}//判断x是左右儿子的哪一个

void rotate(int rt)
{
    int frt=tree[rt].f;
    int ffrt=tree[frt].f;
    push_down(rt);push_down(frt);
    bool w=which(rt);
    tree[frt].son[w]=tree[rt].son[w^1];
    tree[tree[frt].son[w]].f=frt;
    tree[frt].f=rt;
    tree[rt].f=ffrt;
    tree[rt].son[w^1]=frt;
    if (ffrt) tree[ffrt].son[tree[ffrt].son[1]==frt]=rt;
    update(frt);
}

void splay(int rt,int goal)//将rt伸展为goal的儿子
{
    for (int i;(i=tree[rt].f)!=goal;rotate(rt)){
        if (tree[i].f!=goal)
            rotate(which(rt)==which(i)?i:rt);
    }
    if (goal==0) root=rt;
}

void reverse(int l,int r)
{
    l--,r++;
    l=find(l);r=find(r);
    splay(l,0);//将l-1伸展到根节点
    splay(r,l);//将r+1伸展到根节点后继
    int pos=tree[root].son[1];
    pos=tree[pos].son[0];//找到l-r所在的位置
    tree[pos].tag^=1;//打旋转标记
}

void mid_dfs(int rt)//中序遍历输出
{
    push_down(rt);
    if (tree[rt].son[0]) mid_dfs(tree[rt].son[0]);
    if (tree[rt].val!=_max && tree[rt].val!=_min)
        printf("%d ",tree[rt].val);
    if (tree[rt].son[1]) mid_dfs(tree[rt].son[1]);
}

int main()
{
    int n,m;
    scanf ("%d%d",&n,&m);
    a[1]=0;a[n+2]=n+1;
    _max=n+1,_min=0;
    for (int i=1; i<=n; i++) a[i+1]=i;
    root=build(1,n+2,0);
    while (m--){
        int l,r;
        scanf("%d%d",&l,&r);
        reverse(l+1,r+1);
    }
    mid_dfs(root);
    printf("\n");
    return 0;
}
View Code

 

你可能感兴趣的:(洛谷P3391 文艺平衡树---Splay)