Splay区间修改

简介

Splay的伸展操作使Splay在某些方面变得很方便,甚至也能够利用伸展操作来进行区间操作!
ps:本人蒟蒻,写法可能很奇怪:P。
再ps:首先结构体内部的cmp要写的特殊一点(因为k表示位置):

int cmp(int &k)
{
    if (k==son[0]->si+1) return -1;if (k<son[0]->si+1) return 0;
    k-=son[0]->si+1;return 1;
}

如果k往右边走,那么和寻找第k大一样,要减去son[0]->si+1。

序列转Splay

对于一个序列,我们可以把他变成一棵Splay,并使Splay的中序遍历和该序列相同。这样的话,序列中的一段就可以和Splay相对应了。实现如下:
这里写图片描述
递归调用即可。

P_node Build(int L,int R)
{
    if (L>R) return null;
    int mid=L+(R-L>>1);
    P_node now=newNode(mid,a[mid]);
    now->son[0]=Build(L,mid-1);now->son[1]=Build(mid+1,R);Pushup(now);
    return now;
}

定位

有了伸展操作之后,只要把L-1伸展到根,然后把R+1伸展到根的右儿子,L~R这一块区间就被定位在根的右儿子的左儿子了(很好yy,不解释了)。

void Change(P_node &ro,int L,int R)
{
    L--;R++;
    Splay(ro,L);ro->cmp(R);
    Splay(ro->son[1],R);
}

抽取

(本蒟蒻的操作中有个)抽取操作,就是把L~R从当前Splay中脱离。有了定位操作之后很容易写,不要忘记更新信息。

P_node Split(P_node &ro,int L,int R)
{
    Change(ro,L,R);
    P_node now=ro->son[1]->son[0];
    ro->son[1]->son[0]=null;
    ro->son[1]->Pushup();ro->Pushup();
    return now;
}

插入

(本蒟蒻的操作中有个)插入操作,就是把一棵新的Splay插入到当前Splay的L后。

void Insert(P_node &ro,int L,P_node now)
{
    Change(ro,L+1,L);
    ro->son[1]->son[0]=now;
    ro->son[1]->Pushup();ro->Pushup();
}

翻转

记录flip翻转标记,每次Pushdown,就和线段树一样。

总值

同理,用Lazy-tag即可。

模板

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long LL;
const int maxn=100000;

int n,te,a[maxn+5];
//=========================================================================
struct Node
{
    Node *son[2];
    int x,num,si;
    LL tag,sum;
    bool flip;
    int cmp(int &k)
    {
        if (k==son[0]->si+1) return -1;if (k<son[0]->si+1) return 0;
        k-=son[0]->si+1;return 1;
    }
};
typedef Node* P_node;
Node tem[maxn+5];
P_node null=tem,ro=null,P_tem=null;
P_node newNode(int k,int num)
{
    P_tem++;P_tem->x=k;P_tem->si=1;
    P_tem->num=num;P_tem->tag=P_tem->sum=P_tem->flip=0;
    P_tem->son[0]=P_tem->son[1]=null;
    return P_tem;
}
void Pushup(P_node &id)
{
    id->si=id->son[0]->si+id->son[1]->si+1; //更新节点个数
    id->sum=id->son[0]->sum+id->son[1]->sum+id->num; //更新总和
}
void Pushdown(P_node &id)
{
    if (id->flip) //翻转标记
    {
        swap(id->son[0],id->son[1]);id->flip=false;
        if (id->son[0]!=null) id->son[0]->flip^=1;
        if (id->son[1]!=null) id->son[1]->flip^=1;
    }
    LL tag=id->tag;id->tag=0; //加和标记
    if (id->son[0]!=null)
    {
        id->son[0]->sum+=tag*id->son[0]->si;
        id->son[0]->tag+=tag;id->son[0]->num+=tag;
    }
    if (id->son[1]!=null)
    {
        id->son[1]->sum+=tag*id->son[1]->si;
        id->son[1]->tag+=tag;id->son[1]->num+=tag;
    }
}
void LNR(P_node &id) //遍历
{
    if (id==null) return;Pushdown(id);
    LNR(id->son[0]);printf("%d\n",id->x);LNR(id->son[1]);
}
void Rotate(P_node &id,int d)
{
    P_node t=id->son[d^1];id->son[d^1]=t->son[d];t->son[d]=id;
    Pushup(id);Pushup(t);id=t;
}
P_node Build(int L,int R) //将L~R的a序列变成Splay
{
    if (L>R) return null;
    int mid=L+(R-L>>1);
    P_node now=newNode(mid,a[mid]);
    now->son[0]=Build(L,mid-1);now->son[1]=Build(mid+1,R); //递归调用
    Pushup(now); //节点更新
    return now;
}
void Splay(P_node &id,int k)
{
    Pushdown(id); //传递Lazy-tag
    int d1=id->cmp(k);
    if (d1!=-1)
    {
        P_node p=id->son[d1];Pushdown(p); //传递Lazy-tag
        int d2=p->cmp(k);
        if (d2!=-1)
        {
            Splay(p->son[d2],k);
            if (d1==d2) Rotate(id,d1^1),Rotate(id,d1^1); else
            Rotate(id->son[d1],d2^1),Rotate(id,d1^1);
        } else Rotate(id,d1^1);
    }

}
void Change(P_node &ro,int L,int R)
{
    //将L-1旋到根,R+1旋到根的右儿子,则L~R就是根的右儿子的左儿子
    L--;R++;
    Splay(ro,L);ro->cmp(R);
    Splay(ro->son[1],R);
}
void Insert(P_node &ro,int L,P_node now) //在L后插入now
{
    Change(ro,L+1,L); //将L旋到根,L+1旋到根的右儿子,根的右儿子的左儿子必定为空
    ro->son[1]->son[0]=now; //加入now
    Pushup(ro->son[1]);Pushup(ro); //更新节点
}
P_node Split(P_node &ro,int L,int R)
{
    Change(ro,L,R);
    P_node now=ro->son[1]->son[0]; //抽出L~R
    ro->son[1]->son[0]=null; //子树没了
    Pushup(ro->son[1]);Pushup(ro); //更新节点
    return now;
}
//=========================================================================
bool Eoln(char ch) {return ch==10||ch==13||ch==EOF;}
int readi(int &x) //读入优化
{
    int tot=0,f=1;char ch=getchar(),c=ch;
    while ('9'<ch||ch<'0') {if (ch==EOF) return EOF;c=ch;ch=getchar();}
    if (c=='-') f=-f;
    while ('0'<=ch&&ch<='9') tot=tot*10+ch-48,ch=getchar();
    x=tot*f;
    return Eoln(ch);
}
void make_Splay(int L,int R) {ro=Build(L,R);} //初始化Splay
int main()
{
    freopen("Splay.in","r",stdin);
    freopen("Splay.out","w",stdout);
    readi(n);make_Splay(0,n+1);
    //必须在1前面和n后面加两个节点,否则调用L-1和R+1要炸掉,但是由于加了节点,所以调用函数要注意
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值