【BZOJ3223】文艺平衡树

13 篇文章 0 订阅

题面

题目描述

您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1
输入输出格式
输入格式:
第一行为n,m n表示初始序列有n个数,这个序列依次是(1,2,⋯n−1,n),m表示翻转操作次数。接下来m行每行两个数 [l,r] 数据保证 1≤l≤r≤n

输出格式:

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

输入样例

5 3
1 3
1 3
1 4

输出样例

4 3 2 1 5

说明

n,m≤100000 n, m \leq 100000 n,m≤100000

题解

这里的Splay维护的显然不再是权值排序
现在按照的是序列中的编号排序(不过在这道题目里面就是权值诶。。。)
那么,继续考虑,其实最终的结果也就是整颗Splay的中序遍历(平衡树的性质诶)
那么,现在如果按照权值来维护显然是不正确的
继续找找规律,发现,如果一个点在序列中的位置为第K个
那么,他就是平衡树的第K大(就当做普通的Splay来看的话)
所以,序列中的位置就变成了区间的第K大点
继续考虑如何翻转
翻转也就是整颗子树的每一个节点的左右儿子交换
因此,只要在根节点的地方打一个标记
在旋转之前下方一下标记就行了
最后输出的时候输出的就是Splay的中序遍历
至于初始的Splay怎么建立,可以直接构造完美的Splay
像我这种比较懒得,直接弄了一个insert。。。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
#define MAX 200000
inline int read()
{
    int x=0,t=1;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=-1,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return x*t;
}
struct Node
{
    int ch[2];
    int ff,v;
    int size;
    int mark;
    void init(int x,int fa)
        {
            ff=ch[0]=ch[1]=0;
            size=1;v=x;ff=fa;
        }
}t[MAX];
int N,root,M,tot;
inline void pushup(int x)
{
    t[x].size=t[t[x].ch[0]].size+t[t[x].ch[1]].size+1;
}
inline void pushdown(int x)
{
    if(t[x].mark)
    {
        t[t[x].ch[0]].mark^=1;
        t[t[x].ch[1]].mark^=1;
        t[x].mark=0;
        swap(t[x].ch[0],t[x].ch[1]);
    }
}
inline void rotate(int x)
{
    int y=t[x].ff;
    int 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);
}
inline void Splay(int x,int goal)
{
    while(t[x].ff!=goal)
    {
        int y=t[x].ff;int z=t[y].ff;
        if(z!=goal)
            (t[z].ch[1]==y)^(t[y].ch[1]==x)?rotate(x):rotate(y);
        rotate(x);
    }
    if(goal==0)root=x;
}
inline void insert(int x)
{
    int u=root,ff=0;
    while(u)ff=u,u=t[u].ch[x>t[u].v];
    u=++tot;
    if(ff)t[ff].ch[x>t[ff].v]=u;
    t[u].init(x,ff);
    Splay(u,0);
}
inline int Kth(int k)
{
    int u=root;
    while(233)
    {
        pushdown(u);
        if(t[t[u].ch[0]].size>=k)u=t[u].ch[0];
        else if(t[t[u].ch[0]].size+1==k)return u;
        else k-=t[t[u].ch[0]].size+1,u=t[u].ch[1];
    }
}
void write(int u)
{
    pushdown(u);
    if(t[u].ch[0])write(t[u].ch[0]);
    if(t[u].v>1&&t[u].v<N+2)printf("%d ",t[u].v-1);
    if(t[u].ch[1])write(t[u].ch[1]);
}
inline void Work(int l,int r)
{
    l=Kth(l);
    r=Kth(r+2);
    Splay(l,0);
    Splay(r,l);
    t[t[t[root].ch[1]].ch[0]].mark^=1;
}
int main()
{
    N=read();M=read();
    for(int i=1;i<=N+2;++i)insert(i);
    while(M--)
    {
        int l=read(),r=read();
        Work(l,r);
    }
    write(root);
    printf("\n");
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值