950. 郁闷的出纳员
题目链接
950. 郁闷的出纳员
OIER 公司是一家大型专业化软件公司,有着数以万计的员工。
作为一名出纳员,我的任务之一便是统计每位员工的工资。
这本来是一份不错的工作,但是令人郁闷的是,我们的老板反复无常,经常调整员工的工资。
如果他心情好,就可能把每位员工的工资加上一个相同的量。
反之,如果心情不好,就可能把他们的工资扣除一个相同的量。
我真不知道除了调工资他还做什么其它事情。
工资的频繁调整很让员工反感,尤其是集体扣除工资的时候,一旦某位员工发现自己的工资已经低于了合同规定的工资下界,他就会立刻气愤地离开公司,并且再也不会回来了。
每位员工的工资下界都是统一规定的。
每当一个人离开公司,我就要从电脑中把他的工资档案删去,同样,每当公司招聘了一位新员工,我就得为他新建一个工资档案。
老板经常到我这边来询问工资情况,他并不问具体某位员工的工资情况,而是问现在工资第 \(k\) 多的员工拿多少工资。
每当这时,我就不得不对数万个员工进行一次漫长的排序,然后告诉他答案。
好了,现在你已经对我的工作了解不少了。
正如你猜的那样,我想请你编一个工资统计程序。
怎么样,不是很困难吧?
输入格式
第一行有两个非负整数 \(n\) 和 \(min\),\(n\) 表示下面有多少条命令,\(min\) 表示工资下界。
接下来的 \(n\) 行,每行表示一条命令,命令可以是以下四种之一:
I
命令,格式为I_k
,表示新建一个工资档案,初始工资为 \(k\)。如果某员工的初始工资低于工资下界,他将立刻离开公司。A
命令,格式为A_k
,表示把每位员工的工资加上 \(k\)。S
命令,格式为S_k
,表示把每位员工的工资扣除 \(k\)。F
命令,格式为F_k
,表示查询第 \(k\) 多的工资。
_
(下划线)表示一个空格,I
命令、A
命令、S
命令中的 \(k\) 是一个非负整数,F
命令中的 \(k\) 是一个正整数。
在初始时,可以认为公司里一个员工也没有。
输出格式
输出行数为 \(F\) 命令的条数加一。
对于每条 \(F\) 命令,你的程序要输出一行,仅包含一个整数,为当前工资第 \(k\) 多的员工所拿的工资数,如果 \(k\) 大于目前员工的数目,则输出 \(-1\)。
输出文件的最后一行包含一个整数,为离开公司的员工的总数。
注意,如果某个员工的初始工资低于最低工资标准,那么将不计入最后的答案内。
数据范围
I
命令的条数不超过 \(10^5\),
A
命令和 S
命令的总条数不超过 \(100\),
F
命令的条数不超过 \(10^5\),
每次工资调整的调整量不超过 \(1000\),
新员工的工资不超过 \(10^5\)。
输入样例:
9 10
I 60
I 70
S 50
F 2
I 30
S 15
A 5
F 1
F 2
输出样例:
10
20
-1
2
解题思路
splay
由于每次插入一个数,都按照值在中序遍历中的位置插入,而所有的操作都不改变 \(splay\) 所维护的中序遍历的顺序,所以整个操作中 \(splay\) 维护的中序遍历的值是有序的,而本题关键在于删除一个区间,删除一个区间往往引入两个哨兵 \(-inf,inf\),其中 \(L\) 表示值为 \(-inf\) 的节点,不妨用 \(d\) 记下目前增减操作的值,而每次删除节点时,要求删除的节点表示的值 \(x\),满足 \(-inf<x+d<mn\),则需删除 \(splay\) 中表示值为 \([0,mn-d)\) 的节点,则需将这些节点放在一棵子树上便于删除,则需找到最小满足 \(x\geq mn-d\) 的 \(x\) 的代表的节点 \(R\),然后有两种方法将所求区间变为一棵子树:1. \(splay(R,0),splay(L,R)\),此时 \(L\) 的右子树即为所求;2. \(splay(L,0),splay(R,L)\),此时 \(R\) 的左子树即为所求
- 时间复杂度:\(O(nlogn)\)
代码
// Problem: 郁闷的出纳员
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/952/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
// %%%Skyqwq
#include <bits/stdc++.h>
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
const int N=1e5+5,inf=1e8;
int root,cnt,d,n,mn,k,res;
struct Tr
{
int s[2],sz,p,v;
void init(int _p,int _v)
{
p=_p,v=_v;
}
}tr[N];
void pushup(int u)
{
tr[u].sz=tr[tr[u].s[0]].sz+tr[tr[u].s[1]].sz+1;
}
void rotate(int x)
{
int y=tr[x].p,z=tr[y].p;
int k=tr[y].s[1]==x;
tr[z].s[tr[z].s[1]==y]=x,tr[x].p=z;
tr[y].s[k]=tr[x].s[k^1],tr[tr[x].s[k^1]].p=y;
tr[x].s[k^1]=y,tr[y].p=x;
pushup(y),pushup(x);
}
void splay(int x,int k)
{
while(tr[x].p!=k)
{
int y=tr[x].p,z=tr[y].p;
if(z!=k)
{
if((tr[z].s[1]==y)^(tr[y].s[1]==x))rotate(x);
else
rotate(y);
}
rotate(x);
}
if(!k)root=x;
}
int insert(int v)
{
int u=root,p=0;
while(u)p=u,u=tr[u].s[v>tr[u].v];
u=++cnt;
if(p)tr[p].s[v>tr[p].v]=u;
tr[u].init(p,v);
splay(u,0);
return u;
}
int find(int val)
{
int u=root,res=-1;
while(u)
{
if(tr[u].v>=val)res=u,u=tr[u].s[0];
else
u=tr[u].s[1];
}
return res;
}
int get_k(int k)
{
int u=root;
while(u)
{
if(tr[tr[u].s[0]].sz>=k)u=tr[u].s[0];
else if(tr[tr[u].s[0]].sz+1==k)return tr[u].v;
else
k-=tr[tr[u].s[0]].sz+1,u=tr[u].s[1];
}
return -1;
}
int main()
{
scanf("%d%d",&n,&mn);
int L=insert(-inf),R=insert(inf);
while(n--)
{
char op[2];
scanf("%s%d",op,&k);
if(*op=='I')
{
if(k>=mn)insert(k-d),res++;
}
else if(*op=='A')d+=k;
else if(*op=='S')
{
d-=k;
int R=find(mn-d);
// splay(R,0),splay(L,R);
// tr[L].s[1]=0;
// pushup(R),pushup(L);
splay(R,0),splay(L,R);
tr[L].s[1]=0;
pushup(L),pushup(R);
}
else
{
if(tr[root].sz-2<k)puts("-1");
else
printf("%d\n",get_k(tr[root].sz-k)+d);
}
}
printf("%d",res+2-tr[root].sz);
return 0;
}