BZOJ 1552 浅谈SPLAY维护区间最值

本文探讨了如何使用SPLAY数据结构解决区间最值维护的问题,重点在于将SPLAY的思想转化为区间操作。虽然题目相对复杂,但经过分析发现,关键在于将SPLAY维护序列的方式与线段树的最小值维护相结合。通过优化,只需维护包含最小值的节点指针,减少不必要的信息存储。更新和SPLAY操作时需注意指针的pushdown处理。提供的完整代码展示了这一实现过程。

这里写图片描述
世界真的很大
虽然这道题算是splay的比较偏模板题了,但是自己想的话还是有难度
关键是怎么把SPLAY维护东西的思路转换过来,是关键
写起来虽然的却有点多但是却是意外的好调
看题先:

description:

这里写图片描述

input

输入共两行,第一行为一个整数NN表示物品的个数,1<=N<=100000。第二行为N个用空格隔开的正整数,表示N个物品最初排列的编号。

output

输出共一行,N个用空格隔开的正整数P1,P2,P3…Pn,(1 < = Pi < = N),Pi表示第i次操作前第i小的物品所在的位置。 注意:如果第i次操作前,第i小的物品己经在正确的位置Pi上,我们将区间[Pi,Pi]反转(单个物品)。

区间翻转比较好说,关键是怎么在维护序列时同时维护最小值
考虑普通平衡树在维护值域时维护最小值,就是平衡树的最左边
但是这道题维护的并不是值域,而是序列
考虑平衡树维护值域时,不仅可以知道最小值,还可以知道所有的值域信息,而这道题里面只需要知道最小值,维护值域不免有些浪费
考虑在维护序列时平衡树的每一个节点及其子树对应的是序列上连续的一段区间,这不免和线段树有些相似
而线段树维护最小值就只需要维护最小值就行了,所以我们平衡树也需要这么干
但不同的是线段树的结构是不变的,而平衡树的结构是不断改变的,并且为了对应到区间里面去,我们需要维护的,不再是最小值而已,而是包含最小值的节点的指针
注意update的时候多加一个维护最小值指针的东西
SPLAY的时候需要pushdown,原来一直不知道。。。
完整代码:

#include<stdio.h>
#include<stack>
#include<algorithm>
using namespace std;

const int INF=0x3f3f3f3f;

struct number
{
    int val,id;
}aa[200010];

struct node
{
    int sum,flag,siz;
    node *ch[2],*fa,*mst;
    void pushdown(node *nd)
    {
        if(flag)
        {
            swap(ch[0],ch[1]);
            if(ch[0]!=nd) ch[0]->flag^=1;
            if(ch[1]!=nd) ch[1]->flag^=1;
            flag=0;
        }
    }
    void update()
    {
        if(ch[0]->mst->sum < ch[1]->mst->sum) mst=ch[0]->mst;
        else mst=ch[1]->mst;
        if(sum<mst->sum) mst=this ;
        siz=ch[0]->siz+ch[1]->siz+1;
    }
}pool[1000010],*tail=pool,*root,*null;

stack <node*> stk;

int n,m,a[200010],ans[200010];

bool cmp(const number &a,const number &b)
{
    if(a.val==b.val) return a.id<b.id;
    else return a.val<b.val;
}

void init()
{
    null=++tail;
    null->siz=0;
    null->ch[0]=null->ch[1]=null->mst=null;
    null->flag=0;
    null->sum=INF;
}

node* newnode(node *fa)
{
    node *nd=++tail;
    nd->fa=fa;
    nd->siz=1;
    nd->ch[1]=nd->ch[0]=nd->mst=null;
    nd->flag=0;
    return nd;
}

void rot ( node*& x, int d )  
{ 
    node* y=x->fa;
    y->ch[!d]=x->ch[d];
    if(x->ch[d]!= null) x->ch[d]->fa=y ;
    x->fa=y->fa ;
    if(y->fa!=null)
        (y==y->fa->ch[0]) ? y->fa->ch[0]=x : y->fa->ch[1]=x;
    x->ch[d]=y;
    y->fa =x;
    x->update();
    y->update();
}

node *build(node *fa,int lf,int rg)
{
    if(lf>rg) return null;
    node *nd=newnode(fa);
    if(lf==rg)
    {
        nd->sum=a[lf];
        nd->mst=nd;
        return nd;
    }
    int mid=(lf+rg)>>1;
    nd->sum=a[mid];
    nd->mst=nd;
    nd->ch[0]=build(nd,lf,mid-1);
    nd->ch[1]=build(nd,mid+1,rg);
    nd->update();
    return nd;
}


void Splay(node *nd,node *tar)
{
    for(node *i=nd;i!=tar;i=i->fa)
        stk.push(i);
    while(!stk.empty()) stk.top()->pushdown(null) ,stk.pop();
    while(nd->fa!=tar)
    {
        node *ne=nd->fa;
        if(nd==ne->ch[0])
        {
            if(ne->fa!=tar&&ne==ne->fa->ch[0])
               rot(ne,1);
            rot(nd,1);
        }
        else
        {
            if(ne->fa!=tar&&ne==ne->fa->ch[1])
               rot(ne,0);
            rot(nd,0);
        }
    }
    if(tar==null) root=nd;
}

node *kth(node *nd,int K)
{
    nd->pushdown(null);
    if(nd->ch[0]->siz+1==K) return nd;
    if(nd->ch[0]->siz+1>K) return kth(nd->ch[0],K);
    else return kth(nd->ch[1],K-nd->ch[0]->siz-1);
}

void rev(int L,int R)
{
    node *x=kth(root,L);node *y=kth(root,R+2);
    Splay(x,null);
    Splay(y,root);
    y->ch[0]->flag^=1;
}

node *qumin(int pos)
{
    node *x=kth(root,pos),*y=kth(root,n+2);
    Splay(x,null);
    Splay(y,root);
    return y->ch[0]->mst;
}

int main()
{
    init();
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
         scanf("%d",&aa[i].val),aa[i].id=i;
    sort(aa+1,aa+n+1,cmp);
    for(int i=1;i<=n;i++)
         a[aa[i].id]=i;
    a[0]=a[n+1]=INF;
    root=build(null,0,n+1);   
    for(int i=1;i<=n;i++)
    {    
        node *x=qumin(i);
        Splay(x,null);
        ans[i]=x->ch[0]->siz;       
        rev(i,ans[i]);  
    }
    for(int i=1;i<n;i++)
        printf("%d ",ans[i]);
    printf("%d",ans[n]);
    return 0;
}
/*
EL PSY CONGROO
*/

嗯,就是这样

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值