洛谷1486 [NOI2004]郁闷的出纳员 splay

题目

OIER公司是一家大型专业化软件公司,有着数以万计的员工。作为一名出纳员,
我的任务之一便是统计每位员工的工资。这本来是一份不错的工作,但是令人郁
闷的是,我们的老板反复无常,经常调整员工的工资。如果他心情好,就可能把
每位员工的工资加上一个相同的量。反之,如果心情不好,就可能把他们的工资
扣除一个相同的量。我真不知道除了调工资他还做什么其它事情。

工资的频繁调整很让员工反感,尤其是集体扣除工资的时候,一旦某位员工发现
自己的工资已经低于了合同规定的工资下界,他就会立刻气愤地离开公司,并且
再也不会回来了。每位员工的工资下界都是统一规定的。每当一个人离开公司,
我就要从电脑中把他的工资档案删去,同样,每当公司招聘了一位新员工,我就
得为他新建一个工资档案。

老板经常到我这边来询问工资情况,他并不问具体某位员工的工资情况,而是问
现在工资第k多的员工拿多少工资。每当这时,我就不得不对数万个员工进行一次
漫长的排序,然后告诉他答案。

好了,现在你已经对我的工作了解不少了。正如你猜的那样,我想请你编一个
工资统计程序。怎么样,不是很困难吧?

如果某个员工的初始工资低于最低工资标准,那么将不计入最后的答案内

题解

这是本蒟蒻写的第一棵splay呢,啦啦啦

splay,在我的理解里是一种保持二叉查找树(BST)性质的,可以通过旋转操作
将树的高度基本维护在logn,同时可以通过灵活的旋转达到各种操作和目的,平衡树

splay中的左旋和右旋操作和treap是一样的

而splay算法中最核心的部分是splay(x,y)操作,表示把点x旋转到y下面,
当y=0时直接把x旋转到整棵树的根。
splay操作用于维护树的平衡,
每次查找某个点,插入某个点,都要把该点splay到树根,
同时也可用于处理一些特殊操作
splay操作具体步骤如下各图(要旋转的点为儿子点)
1.父亲点为整棵树的根时,直接左旋或右旋一次,下图为右旋示例
这里写图片描述
2.当儿子点和父亲点同为做儿子或右儿子时,先旋父亲点,然后旋祖父点
下图为右旋示例,左旋则类似
这里写图片描述
3.当儿子点是左儿子,父亲点是右儿子时,先右旋父亲点,再左旋祖父点
以此类推,当儿子点是右儿子,父亲点是左儿子时,先左旋父亲点,然后右旋祖父点
这里写图片描述
splay操作要注意细节,左旋操作和右旋操作不要弄反了,可以一边画图一边写。
(555~我就是旋转错了好久,导致tle)

插入insert
先找出适合插入点x的叶子节点位置,然后插入,并将x点splay到树根

查找find
和treap一样,但查找完后要将该点splay到树根

删除del
这题是删除一个1—x的区间,所以把x的父亲splay到树根,直接把左子树删除
当只删除一个点时,首先找到这个数的前驱,把他Splay到根节点
然后找到这个数后继,把他旋转到前驱的底下
比前驱大的数是后继,在右子树
比后继小的且比前驱大的有且仅有当前数
在后继的左子树上面,
因此直接把当前根节点的右儿子的左儿子删掉就可以啦

以下摘自Crash的《运用伸展树解决数列维护问题》
到目前为止,伸展树只是实现了线段树能够实现的功能,下面两个功能将是
线段树无法办到的。
如果我们要在a 后面插入一些数,那么我们先把这些插入的数建成一棵伸展
树,我们可以利用分治法建立一棵完全平衡的二叉树,就是说每次把最中间的作
为当前区间的根,然后左右递归处理,返回的时候进行维护。接着将a 转到根,
将a 后面一个数对应的结点转到根结点的右边,最后将这棵新的子树挂到根右子
结点的左子结点上。
还有一个操作就是删除一个区间a,b内的数,像上面一样,我们先提取区
间,然后直接删除那棵子树,即可达到目的。
最后还需注意的就是,每当进行一个对数列进行修改的操作后,都要维护伸
展树,一种方法就是对影响到的结点从下往上执行Update 操作。但还有一种方
法,就是将修改的结点旋转到根,因为Splay 操作在旋转的同时也会维护每个结
点的值,因此可以达到对整个伸展树维护的目的。
最后还有一个小问题,因为数列中第一个数前面没有数字了,并且最后一个
数后面也没有数字了,这样提取区间时就会出一些问题。为了不进行过多的特殊
判断,我们在原数列最前面和最后面分别加上一个数,在伸展树中就体现为结点,
这样提取区间的时候原来的第k个数就是现在的第k 1个数。并且我们还要注
意,这两个结点维护的信息不能影响到正确的结果。

代码

#include <cstdio>

using namespace std;

struct node{
    int l,r,size,data,fa;
}t[100005];
int n,m,add,ans,root,tot;

void updata(int x){
    t[x].size=t[t[x].l].size+t[t[x].r].size+1;
}

void rttn(int x){
    int y=t[x].l;
    if (t[t[x].fa].l==x) t[t[x].fa].l=y;
                    else t[t[x].fa].r=y;
    t[y].fa=t[x].fa;
    t[x].fa=y;
    t[t[y].r].fa=x;
    t[x].l=t[y].r;
    t[y].r=x;
    updata(x);updata(y);
    if (x==root) root=y;
}

void lttn(int x){
    int y=t[x].r;
    if (t[t[x].fa].l==x) t[t[x].fa].l=y;
                    else t[t[x].fa].r=y;
    t[y].fa=t[x].fa;
    t[x].fa=y;
    t[x].r=t[y].l;
    t[y].l=x;
    t[t[x].r].fa=x;
    updata(x);updata(y);
    if (x==root) root=y;
}

void splay(int x){  
    while (x!=root){
        int f=t[x].fa,p=t[f].fa;
        if (f==root)
            if (t[f].l==x) rttn(f); else lttn(f);
        else 
        if (t[f].l==x){
            if (t[p].l==f) {rttn(f);rttn(p);}
                      else {rttn(f);lttn(p);}
        } else
        if (t[p].r==f) {lttn(f);lttn(p);}
                  else {lttn(f);rttn(p);}
    }
}

void insert(int k){
    if (!root){
        root=++tot;
        t[tot].data=k;
        t[tot].size=1;
        return;
    }
    int x=root,y=0;
    while (x){
        y=x;
        t[x].size++;
        if (t[x].data>k) x=t[x].l;
                    else x=t[x].r;
    }
    t[++tot].data=k;
    t[tot].size=1;
    t[tot].fa=y;
    if (t[y].data>k) t[y].l=tot; 
                else t[y].r=tot;
    splay(tot);
}

void del(){
    int x=root,y=0,c=0;;
    while (x){
        if (t[x].data+add<m) x=t[x].r; else
            y=x,x=t[x].l;
    }
    if (!y){
        ans+=t[root].size;
        root=0;
        return;
    }
    splay(y);
    if (t[y].l) ans+=t[t[y].l].size;
    t[y].l=0;
    t[y].size=t[t[y].r].size+1;
}

int find(int k){
    int x=root;
    while (t[t[x].l].size+1!=k){
        if (t[t[x].l].size+1<k) 
            k-=t[t[x].l].size+1,x=t[x].r;
        else x=t[x].l;
    }
    splay(x);
    return t[x].data;
}

int main(){
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++){
        char ch[2];
        int x;
        scanf("%s%d",ch,&x);
        if (ch[0]=='I') 
            if (x>=m) insert(x-add);
        if (ch[0]=='A') add+=x;
        if (ch[0]=='S') add-=x,del();
        if (ch[0]=='F') 
            if (x>tot-ans) printf("-1\n");
                      else printf("%d\n",find(tot-ans-x+1)+add);
    }
    printf("%d\n",ans);
}

猜你喜欢

转载自blog.csdn.net/yjy_aii/article/details/80901076