bzoj3223

Tyvj 1729 文艺平衡树

 HYSBZ - 3223 

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

Input

第一行为n,m n表示初始序列有n个数,这个序列依次是(1,2……n-1,n)  m表示翻转操作次数
接下来m行每行两个数[l,r] 数据保证 1<=l<=r<=n 

Output

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

Sample Input
5 3
1 3
1 3
1 4

Sample Output

4 3 2 1 5

Hint

N,M<=100000

 

sol:splay区间翻转的板子题,似乎搞了很久qaq

要支持在平衡树上打标记,下传的时候就是询问的时候下传,比方说查询第K个的数的位置

翻转的时候打标记就可以了,如果有标记下传的时候交换左右儿子就相当于翻转了(因为是中序遍历)

Q:交换左右儿子以后不满足左边全部比中间小,右边全部大怎么办?

A:不用管,因为没有插入操作了,查找第K个只要管好Size就可以了(确切的说不是第K大的数,只是中序遍历第K个)

Q:怎么找到[l,r]这段区间?

A:找到 l-1 和 r+1 的位置 ll , rr,把 ll 串到根上,这时所有比在 l-1 之后的都在 ll 的右子树里,再把 rr 串到 ll 上所有比l-1后及比rr+1前的就到rr的左子树里了

完结撒花!!!

 


 

代码非常丑丑丑丑丑丑丑丑丑丑丑丑丑丑丑

 

#include <bits/stdc++.h>
using namespace std;
typedef int ll;
inline ll read()
{
    ll s=0;
    bool f=0;
    char ch=' ';
    while(!isdigit(ch))
    {
        f|=(ch=='-'); ch=getchar();
    }
    while(isdigit(ch))
    {
        s=(s<<3)+(s<<1)+(ch^48); ch=getchar();
    }
    return (f)?(-s):(s);
}
#define R(x) x=read()
inline void write(ll x)
{
    if(x<0)
    {
        putchar('-'); x=-x;
    }
    if(x<10)
    {
        putchar(x+'0'); return;
    }
    write(x/10);
    putchar((x%10)+'0');
    return;
}
#define W(x) write(x),putchar(' ')
#define Wl(x) write(x),putchar('\n')
const int N=200005,inf=0x3f3f3f3f;
int n,m;
namespace Pht
{
    int Points=0,Root=0;
    int Child[N][2];
    int Parent[N];
    int Quanzhi[N];
    int Size[N];
    bool Rev[N];
    
    inline void Init();
    inline int Check(int x);
    inline void PushUp(int x);
    inline void PushDown(int x);
    inline void Rotate(int x);
    inline void Splay(int At,int To);
    inline void Insert(int Val);
    inline int Kth(int Id);
    inline void OutPut(int Now);
    inline void Solve();
    
    inline void Init()
    {
        Points=Root=0;
        Insert(-inf);
        Insert(inf);
    }
    inline int Check(int x)
    {
        return (Child[Parent[x]][0]==x)?0:1;
    }
    inline void PushUp(int x)
    {
        Size[x]=Size[Child[x][0]]+Size[Child[x][1]]+1;
    }
    inline void PushDown(int x)
    {
        if((!x)||(!Rev[x])) return;
        swap(Child[x][0],Child[x][1]);
        Rev[x]=0;
        Rev[Child[x][0]]^=1;
        Rev[Child[x][1]]^=1;
    }
    inline void Rotate(int x)
    {
        int y,z,oo;
        y=Parent[x];
        z=Parent[y];
        oo=Check(x);
        Child[y][oo]=Child[x][oo^1]; Parent[Child[x][oo^1]]=y;
        Child[z][Check(y)]=x; Parent[x]=z;
        Child[x][oo^1]=y; Parent[y]=x;
        PushUp(x); PushUp(y);
    }
    inline void Splay(int At,int To)
    {
        while(Parent[At]!=To)
        {
            int Father=Parent[At];
            if(Parent[Father]==To)
            {
                Rotate(At);
            }
            else if(Check(At)==Check(Father))
            {
                Rotate(Father); Rotate(At);
            }
            else
            {
                Rotate(At); Rotate(At);
            }
        }
        if(To==0) Root=At;
    }
    inline void Insert(int Val)
    {
        int Now=Root,Par=0;
        while(Now)
        {
            Par=Now;
            Now=Child[Now][(Val>Quanzhi[Now])?1:0];
        }
        Now=++Points;
        if(Par) Child[Par][(Val>Quanzhi[Par])?1:0]=Now;
        Child[Now][0]=Child[Now][1]=0;
        Parent[Now]=Par;
        Size[Now]=1;
        Quanzhi[Now]=Val;
        Rev[Now]=0;
        Splay(Now,0);
    }
    inline int Kth(int Id)
    {
        int Now=Root;
        for(;;)
        {
            PushDown(Now);
            if(Size[Child[Now][0]]>=Id)
            {
                Now=Child[Now][0];
            }
            else if(Size[Child[Now][0]]+1==Id)
            {
                return Now;
            }
            else if(Size[Child[Now][0]]+1<Id)
            {
                Id-=(Size[Child[Now][0]]+1);
                Now=Child[Now][1];
            }
        }
    }
    inline void Reverse(int l,int r)
    {
        int ll=Kth(l),rr=Kth(r+2); //找到l-1和r+1的位置
        Splay(ll,0); //所有在l-1之后的都到ll的右子树 
        Splay(rr,ll); //在ll的基础上所有比r+1前的都到rr的左子树,所以[l,r]这段区间就是rr的左子树
        Rev[Child[rr][0]]^=1;
    }
    inline void OutPut(int Now)
    {
        PushDown(Now);
        if(Child[Now][0]) OutPut(Child[Now][0]);
        if(Quanzhi[Now]>=1&&Quanzhi[Now]<=n) W(Quanzhi[Now]);
        if(Child[Now][1]) OutPut(Child[Now][1]);
    }
    inline void Solve()
    {
        int i;
        Init();
        for(i=1;i<=n;i++) Insert(i);
        while(m--)
        {
            int l=read(),r=read();
            Reverse(l,r);
        }
        OutPut(Root);
    }
}
int main()
{
    R(n); R(m);
    Pht::Solve();
    return 0;
}
/*
input
5 3
1 3
1 3
1 4
output
4 3 2 1 5
*/
View Code

 

 

Ps:我写的似乎是不重的区间翻转,重复的应该也差不多,毕竟只有Insert要用到,大不了一个个插(全是口胡)

 

转载于:https://www.cnblogs.com/gaojunonly1/p/10667049.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值