郁闷的出纳员 题解(Splay)

题面

看似是要区间修改,然而实际上只需要维护底线和工资的相对大小关系,

瞬间变水

用delta记录对工资的加减,那么添加节点时点权应-delta,输出时+delta

几种操作中减少工资较麻烦:

1.delta-=val;

2.删点

求前驱转到根,删除左子树

这里的删除不用一个一个暴力删,直接断掉子树关系即可

至于求k大 我比较懒直接改成求size-k+1小 (逃)

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int N=1e5+5;
int n,fa[N],cnt[N],son[N][3],size[N],key[N],type,root;
int line,ans,num,delta;
void clear(int x)
{
    fa[x]=cnt[x]=son[x][1]=son[x][0]=size[x]=key[x]=0;
}
bool judge(int x)
{
    return son[fa[x]][1]==x;
}
void up(int x)
{
    if(x)
    {
        size[x]=cnt[x];
        if(son[x][0])size[x]+=size[son[x][0]];
        if(son[x][1])size[x]+=size[son[x][1]];
    }
}
void rotate(int x)
{
    int old=fa[x],oldf=fa[old],lr=judge(x);
    son[old][lr]=son[x][lr^1];
    fa[son[old][lr]]=old;
    son[x][lr^1]=old;
    fa[old]=x;
    fa[x]=oldf;
    if(oldf)son[oldf][son[oldf][1]==old]=x;
    up(old);up(x);
}
void splay(int x)
{
    for(int f;f=fa[x];rotate(x))
        if(fa[f])rotate(judge(x)==judge(f)?f:x);
    root=x;
}
void ins(int x)
{
    if(!root)
    {
        type++;
        key[type]=x;
        root=type;
        cnt[type]=size[type]=1;
        fa[type]=son[type][0]=son[type][1]=0;
        return ;
    }
    int now=root,f=0;
    while(1)
    {
        if(x==key[now])
        {
            cnt[now]++;
            up(now);
            up(f);
            splay(now);
            return ;
        }
        f=now;now=son[now][key[now]<x];
        if(!now)
        {
            type++;
            size[type]=cnt[type]=1;
            son[type][0]=son[type][1]=0;
            son[f][x>key[f]]=type;
            fa[type]=f;
            key[type]=x;
            up(f);splay(type);
            return ;
        }
    }
}
int getnum(int x)
{
    int now=root;
    while(1)
    {
        if(son[now][0]&&x<=size[son[now][0]])now=son[now][0];
        else
        {
            int tmp=size[son[now][0]]+cnt[now];
            if(x<=tmp)
                return key[now];
            x-=tmp;now=son[now][1];
        }
    }
}
int pre()
{
    if(cnt[root]>1)return root;
    int now=son[root][0];
    while(son[now][1])now=son[now][1];
    return now;
}
void del(int x)
{
    //changeroot(x);
    if(cnt[root]>1)
    {
        cnt[root]--;
        up(root);
        return ;
    }
    if(!son[root][0]&&!son[root][1])
    {
        clear(root);
        root=0;
        return ;
    }
    if(!son[root][0])
    {
        int old=root;
        root=son[root][1];
        fa[root]=0;
        clear(old);
        return ;
    }
    else if(!son[root][1])
    {
        int old=root;
        root=son[root][0];
        fa[root]=0;
        clear(old);
        return ;
    }
    int old=root,L=pre();
    splay(L);
    son[root][1]=son[old][1];
    fa[son[old][1]]=root;
    clear(old);
    up(root);
}
void del_tree()
{
    fa[son[root][0]]=0;
    size[root]-=size[son[root][0]];
    son[root][0]=0;
}
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9')
    {if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')
    {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    return x*f;
}
int main()
{
    n=read();line=read();
    char op[3];int val; 
    for(int i=1;i<=n;i++)
    {
        scanf("%s",op);val=read();
        switch(op[0])
        {
            case 'I':
            if(val<line)break;
            else
            {
                ins(val-delta);
                break;
            }
            case 'A':
            delta+=val;break;
            case 'S':
            delta-=val;
            ins(line-delta);
            ans+=son[root][0]?size[son[root][0]]:0;
            del_tree();
            del(val-delta);
            break;
            case 'F':
            if(size[root]<val)puts("-1");
            else printf("%d
",getnum(size[root]-val+1)+delta);
            break;
        }
    }
    cout<<ans<<endl;
    return 0;
}
原文地址:https://www.cnblogs.com/Rorschach-XR/p/11015682.html