Tyvj 1729 文艺平衡树(Splay区间翻转+LAZY+优化建树)

题意:传送门

题解:

本质:翻转区间就是翻转左右的子节点。把l翻转到根节点,把r+2翻转到根节点的右节点去,那么l-r这段区间就会集中在根节点右节点的左节点里面,可以画图进行验证。

优化:但是会发现一个问题就是,如果一个区间翻转多次,岂不是很亏,不妨像线段树那样加个lazy标记,用时进行下放。这个题还有个好的优化,就是如何建树,如果一个一个插入确实可以,然后插完后会是个斜树,但是这个题是死的,所以像线段树那样建树,非常巧妙,这种方法在替罪羊树里,重新建树也是用这种方法,最后中序输出进行特判即可。

思维:这个题最关键的地方是要理解splay的操作,这不是寻常的权值搜索,而是根据位置进行操作,使用的是Kth操作,其实也是对splay的一种本质上的理解。

技巧:但是还有一个问题就是如果翻转1-n怎么办呢?那么就得在两边建立两个哨兵节点,这样就能解决所有问题了。
附上代码:

#include<bits/stdc++.h>
#define il inline
using namespace std;
il int read()
{
    char ch=getchar();
    int f=1,x=0;
    while(!(ch>='0'&&ch<='9')){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
const int maxn=1e5+5;
const int inf=0x7fffffff;
struct node{int ch[2],ff,v,size,mark;}t[maxn];
int n,m,root,tot,val[maxn];
il void pushup(int x)
{
    t[x].size=t[t[x].ch[0]].size+t[t[x].ch[1]].size+1;
}
il 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);
}
il 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;
}
int build(int l,int r,int fa)
{
    if(l>r)return 0;
    int mid=(l+r)>>1;
    int now=++tot;
    t[now].size=1;
    t[now].mark=0;
    t[now].v=val[mid];
    t[now].ch[0]=build(l,mid-1,now);
    t[now].ch[1]=build(mid+1,r,now);
    t[now].ff=fa;
    pushup(now);
    return now;
}
il 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]);
    }
}
il 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];
    }
}
il 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;
}
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)printf("%d ",t[u].v);
    if(t[u].ch[1])write(t[u].ch[1]);
}
int main()
{
    n=read();m=read();
    val[1]=-inf,val[n+2]=inf;
    for(int i=2;i<=n+1;i++)val[i]=i-1;
    root=build(1,n+2,0);
    while(m--)
    {
        int l=read(),r=read();
        Work(l,r);
    }
    write(root);
    printf("\n");
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值