题意:

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

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

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

  老板经常到我这边来询问工资情况,他并不问具体某位员工的工资情况,而是问现在工资第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. UVA-11054(扫描法)

    题意: n个等距村庄,每个村庄要么买酒要么卖酒,把k个单位的酒运到相邻村庄去需要k个单位的劳动力,问最少需要多少劳动力才能满足所有的村庄的要求; 思路: 上次做了一个环的,这个是直线的,就是一个大水题 ...

  2. CollectionView垂直缩放卡片布局

    实现效果 实现思路 从效果图可以看到变化是,越是往中间滚动的item显示最大,越显眼.而越是往前面,或者越是后面的,反而显示越小,这样就形成了视觉差. 实现的思路就是通过重写在可见范围内的所有item ...

  3. June Challenge 2017

    A Good Set 分析:水题,选奇数即可 #include "iostream" #include "cstdio" #include "cstr ...

  4. How to study Watir?

    我在群空间,总是看到很多Watir新手,反复的问:我对元素的定位怎么又出错?我该从哪里着手啊?我给一个我认为最简单方便的学习方法.1. Ruby语法基础要入门,网上有一个不到2M的帮助文档,从头到尾仔 ...

  5. laravel5.2 增加Caffienate Modules,实现模块化开发

    1.模块化开发可以把框架分成 Topc前台模块,Topm手机端前台,Admin后台管理模块,每个模块中都有自己的一套Controller,Logic,router等. 2.咖啡因模块是一个简单的包,以 ...

  6. Exception in thread "main" org.hibernate.MappingException: Unknown entity: com.mao.PersonSet

    转自:https://blog.csdn.net/vipmao/article/details/51334743

  7. jzoj5980. 【WC2019模拟12.27】字符串游戏

    首先发现双方可以有一个默契,不管谁刻意,都可以把串变为诸如\(...101010101...\)的形式 所以先手要赢的话就是要在上面的基础之上加一个字符使其变为要求的子串 那么就是要求的子串中相邻两个 ...

  8. 6.while循环正向反向打印一句话

    message = "伤情最是晚凉天,憔悴厮人不堪言."' count=0 num=-1 while count<len(message): print(message[co ...

  9. 鸟哥私房菜基础篇:vim 程序编辑器习题

    猫宁!!! 参考链接:http://cn.linux.vbird.org/linux_basic/0310vi.php 鸟哥是为中国信息技术发展做出巨大贡献的人. 1-我用 vi 开启某个档案后,要在 ...

  10. 安装 synaptic on ubuntu 18

    apt的图形化界面管理 sudo apt install synaptic 安装后使用需要注意的是 如果打开了synaptic,终端中apt命令某些是没法正常用的,比如说apt remove,应该是锁 ...