题意:

  作为一名出纳员,我的任务之一便是统计每位员工的工资。但是我们的老板反复无常,经常调整员工的工资。如果他心情好,就可能把每位员工的工资加上一个相同的量。反之,如果心情不好,就可能把他们的工资扣除一个相同的量。

  工资的频繁调整很让员工反感,尤其是集体扣除工资的时候,一旦某位员工发现自己的工资已经低于了合同规定的工资下界,他就会立刻气愤地离开公司,并且再也不会回来了。

  每位员工的工资下界都是统一规定的。每当一个人离开公司,我就要从电脑中把他的工资档案删去,同样,每当公司招聘了一位新员工,我就得为他新建一个工资档案。

  老板经常到我这边来询问工资情况,他并不问具体某位员工的工资情况,而是问现在工资第k多的员工拿多少工资。

  

思路:

  (1)插入比较简单,只要允许重复值的存在就行了。但是下面的操作需要维护一个变化量add,所以插入之前要先减去add再插入。若不这样做,由于每次加工资只是针对当前的员工,而之前的加减工资并不没有针对新员工,所以新员工并不应该享受到老员工的加减工资福利。

  (2)集体加工资,可以用一个全局变量统计当前所有员工的工资变化量add。

  (3)扣工资同加工资一样,用同一个变量统计。但是有人可能会因为此次扣工资而离开公司,所以要及时清理掉这些人。方法是,如果当前员工有人的工资刚好等于min,那么将其伸展到根,再删除其左子树即可(注意,可能有多个人的工资等于min,那么你删除时,要保证根的左子树中并不存在工资为min的人,即需要将最左的min伸展到根)。否则,插入一个工资为min的点,再将其伸展到根,同样删除左子树,然后再删除自己。注意,所有人的工资得加上所维护的工资变化量。

  (4)查询第k多比较简单,只要维护左子树的节点数量以及右子树的节点数量。如果在左子树中,则k要减去工资大于本节点的人数,再往下找。如果在右子树中,仍然用的是k来找。

  还有一点很神奇的地方就是,每次插入新元素都是插到叶子节点,但是插入过程我们不需要更新插入路径上面的点的左右孩子数量,因为一插入完毕之后立刻就splay到根了,这样子相当于插入之前的树自己在维护孩子数量而已,而新节点从叶子伸展到根自然会更新孩子数量了。所以只需要在rotate中维护孩子数量即可。

 #include <bits/stdc++.h>
#define pii pair<int,int>
#define INF 0x7f7f7f7f
#define LL long long
using namespace std;
const int N=;
int root, node_cnt, ans, add, del;
struct node
{
int pre, val;
int son[]; //子树中有多少个节点。
int ch[];
}nod[N]; int create_node(int v,int far) //返回节点下标
{
nod[node_cnt].val=v;
nod[node_cnt].pre=far;
nod[node_cnt].ch[]=;
nod[node_cnt].ch[]=;
nod[node_cnt].son[]=;
nod[node_cnt].son[]=;
return node_cnt++;
} void init()
{
add=root=node_cnt=;
create_node(INF, ); //0号点是不要的
} void Rotate(int t, int d) //d为方向,0是左旋,1是右
{
int far=nod[t].pre;
int son=nod[t].ch[d]; //far的孩子
int gra=nod[far].pre; //far的父亲 nod[son].pre=far;
nod[t].pre=gra;
nod[far].pre=t; nod[far].ch[d^]=son;
nod[t].ch[d]=far;
nod[gra].ch[nod[gra].ch[]==far]=t; //子树中的节点要更新
nod[far].son[d^]=nod[t].son[d];
nod[t].son[d]+=+nod[far].son[d]; //别忘了还有far也是个节点
} void Splay(int t,int goal) //将t转为goal的任一孩子
{
while( nod[t].pre!=goal ) //t还不是根
{
int f=nod[t].pre, g=nod[f].pre;
if( g==goal ) Rotate( t, nod[f].ch[]==t ); //父亲就是根s,旋1次
else
{
int d1=(nod[f].ch[]==t), d2=(nod[g].ch[]==f);
if( d1==d2 ) //两次同向旋转
{
Rotate( f, d1);
Rotate( t, d1);
}
else //两次反向旋转
{
Rotate( t, d1);
Rotate( t, d2);
}
}
}
if(!goal) root=t; //时刻更新树根
} int Insert(int t, int v)
{
int q=-;
if( v>nod[t].val ) //右边
{
if( nod[t].ch[]== ) q=(nod[t].ch[]=create_node(v, t));
else q=Insert(nod[t].ch[], v);
}
else //左边,相等时插左边
{
if( nod[t].ch[]== ) q=(nod[t].ch[]=create_node(v, t));
else q=Insert(nod[t].ch[], v);
}
return q;
} int Find(int t,int v) //找到值为v的,若没有,则返回-1
{
while( t )
{
if(nod[t].val==v)
{
int r=Find(nod[t].ch[], v); //找到最左边的那一个,即保证t的左子树中没有等于v的点。
if(r==-) return t;
else t=r;
}
if( nod[t].val<v ) //左边
t=nod[t].ch[];
else
t=nod[t].ch[];
}
return -;
} void Delete(int t, int limit) //将所有工资低于限额的,删去该子树。
{
//先找找看有没有等于这个值的。
int r=Find(root, limit-add);
if(r==-) //没有找到,则插入这样的值,Splay到顶,然后删去此点的左子树
{
Splay( Insert( root, limit-add ) , );
del+=nod[root].son[];
int right=nod[root].ch[]; //再删去此节点(即根)
if(right==) init(); //全部删完,没有员工
else nod[right].pre=, root=right;
}
else //找到了最左端的一个。
{
Splay(r, ); //旋转到顶,删去左子树。
del+=nod[root].son[];
nod[root].son[]=;
}
} int Query(int t,int k) //查找第k多
{
if( nod[t].son[]+nod[t].son[]+<k ) return -; //整棵树都还没有k个
while( nod[t].son[]!=k- )
{
if(nod[t].son[]>k-) t=nod[t].ch[]; //在右孩子中
else //在左孩子中
{
k-=nod[t].son[]+;
t=nod[t].ch[];
}
}
Splay(t, );
return add+nod[t].val;
} int main()
{
//freopen("input.txt", "r", stdin);
int n, limit, t;char c;
while(cin>>n>>limit)
{
init();
del=; //离开员工的人数
for(int i=,a=; i<n; i++,a=)
{
while( !isalpha(c=getchar())) ;
scanf("%d", &a);
if(c=='I' && a>=limit) Splay( Insert(root, a-add), ); //插完就伸展。新员工要减掉个add。再伸展到根。
else if(c=='A') add+=a; //全体加工资
else if(c=='S') add-=a,Delete(root, limit); //全体扣工资,有人可能因为此次扣工资而离开公司。
else if(c=='F') printf("%d\n", Query(root, a));
}
printf("%d\n", del);
}
return ;
} AC代码

AC代码

HYSBZ 1503 郁闷的出纳员 (Splay树)的更多相关文章

  1. bzoj 1503郁闷的出纳员(splay)

    1503: [NOI2004]郁闷的出纳员 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 11759  Solved: 4163[Submit][Stat ...

  2. [BZOJ 1503]郁闷的出纳员(fhq treap)

    [BZOJ 1503]郁闷的出纳员 题面 第一行有两个非负整数n和min.n表示下面有多少条命令,min表示工资下界. 接下来的n行,每行表示一条命令.命令可以是以下四种之一: 名称 格式 作用 I命 ...

  3. BZOJ 1503: [NOI2004]郁闷的出纳员 splay

    1503: [NOI2004]郁闷的出纳员 Description OIER公司是一家大型专业化软件公司,有着数以万计的员工.作为一名出纳员,我的任务之一便是统计每位员工的工资.这本来是一份不错的工作 ...

  4. 洛谷 1486/BZOJ 1503 郁闷的出纳员

    1503: [NOI2004]郁闷的出纳员 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 13866  Solved: 5069[Submit][Stat ...

  5. NOI2004 郁闷的出纳员 Splay

    郁闷的出纳员 [问题描述] OIER公司是一家大型专业化软件公司,有着数以万计的员工.作为一名出纳员,我的任务之一便是统计每位员工的工资.这本来是一份不错的工作,但是令人郁闷的是,我们的老板反复无常, ...

  6. BZOJ 1503 郁闷的出纳员 (treap)

    1503: [NOI2004]郁闷的出纳员 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 13370  Solved: 4808[Submit][Stat ...

  7. BZOJ 1503 郁闷的出纳员(splay)

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1503 题意:给出一个数列(初始为空),给出一个最小值Min,当数列中的数字小于Min时自动 ...

  8. 【BZOJ1503】 [NOI2004]郁闷的出纳员 splay

    splay模板题,都快把我做忧郁了. 由于自己调两个坑点. 1.删除时及时updata 2.Kth 考虑k满足该点的条件即r->ch[1]->size+1<=k && ...

  9. BZOJ 1503 郁闷的出纳员(平衡树)(NOI 2004)

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1503 Description OIER公司是一家大型专业化软件公司,有着数以万计的员工.作 ...

随机推荐

  1. dba操作之archivelog清理

    下面的命令用于校验归档日志的有效性,列出无效的归档日志,以及以何种方式清除归档日志,列出几种常用的: crosscheck archivelog all;                        ...

  2. codeforces 696B B. Puzzles(树形dp+概率)

    题目链接: B. Puzzles time limit per test 1 second memory limit per test 256 megabytes input standard inp ...

  3. VC++读写文件

    目录 第1章读写文件    1 1.1 API    1 1.2 低级IO    1 1.2.1 文件序号    1 1.2.2 文本文件与二进制文件    1 1.3 流IO    2 1.4 Un ...

  4. Linux命令排查线上问题常用的几个

    排查线上问题常用的几个Linux命令 https://www.cnblogs.com/cjsblog/p/9562380.html top 相当于Windows任务管理器 可以看到,输出结果分两部分, ...

  5. 洛谷P2473奖励关——状压DP

    题目:https://www.luogu.org/problemnew/show/P2473 还是对DP套路不熟悉... 像这种前面影响后面,而后面不影响前面的问题就应该考虑倒序递推: 看n只有15那 ...

  6. 微信小程序 WXML、WXSS 和JS介绍及详解

    前几天折腾了下.然后列出一些实验结果,供大家参考. 百牛信息技术bainiu.ltd整理发布于博客园 0. 使用开发工具模拟的和真机差异还是比较大的.也建议大家还是真机调试比较靠谱. 1. WXML( ...

  7. HTTP node静态资源请求加载demo

    MIME type的缩写为(Multipurpose Internet Mail Extensions)代表互联网媒体类型(Internet media type),MIME使用一个简单的字符串组成, ...

  8. 装饰器模式(Decorator) C++

    装饰器模式是比较常用的一种设计模式,Python中就内置了对于装饰器的支持. 具体来说,装饰器模式是用来给对象增加某些特性或者对被装饰对象进行某些修改. 如上图所示,需要被装饰的对象在最上方,它自身可 ...

  9. ORACLE PL/SQL 实例精解之第三章 PL/SQL中的SQL

    3.1 在PL/SQL中使用DML 在PL/SQL语块中,两种变量赋值a. := 初始化.b. select into语法,PL/SQL语块的声明部分被声明的变量,后期可以使用选择语句进行赋值. 3. ...

  10. PTA QQ Account Manageme【map的巧妙应有】

    5-27 QQ Account Management (25分) You are supposed to implement the functions of account "Log in ...