P1486 [NOI2004] 郁闷的出纳员

P1486 [NOI2004] 郁闷的出纳员

郁闷的到底是出纳员还是我???

[NOI2004] 郁闷的出纳员

题目描述

输入格式

第一行有两个整数 \(n\)\(\min\)\(n\) 表示下面有多少条命令,\(\min\) 表示工资下界。

接下来的 \(n\) 行,每行一个字符 \(x\) 和一个整数 \(k\),表示一条命令。命令可以是以下四种之一:

  • I k 新建一个工资档案,初始工资为 \(k\)。如果某员工的初始工资低于工资下界,他将立刻离开公司。
  • A k 把每位员工的工资加上 \(k\)
  • S k 把每位员工的工资扣除 \(k\)
  • F k 查询第 \(k\) 多的工资。

在初始时,可以认为公司里一个员工也没有。

输出格式

对于每条 F 命令,你的程序要输出一行,仅包含一个整数,为当前工资第 \(k\) 多的员工所拿的工资数,如果 \(k\) 大于目前员工的数目,则输出 \(-1\)

输出的最后一行包含一个整数,为离开公司的员工的总数。

请注意,初始工资低于工资下界的员工不算做离开公司的员工。

数据规模与约定

对于全部的测试点,保证:

  • I 命令的条数不超过 \(10^5\)
  • AS 命令的总条数不超过 \(100\)
  • F 命令的条数不超过 \(10^5\)
  • 每次工资调整的调整量不超过 \(10^3\)
  • 新员工的工资不超过 \(10^5\)
  • \(0 \leq n \leq 3 \times 10^5\)\(0 \leq \text{min} \leq 10^9\),输入的所有数字均在 \(32\) 位带符号整形范围内。

solution:

平衡树练手题,我们维护一颗平衡树。
观察到A和S的命令数十分的少,我们对于每次命令S:
直接把减掉k后不符合条件的点删掉
用FHQ-Treap十分好写就是了

然后对于每次A和S,对应剩下的节点dfs一下来更新权值(由于操作次数少,基本不用担心超时)

然后对于每次查询,直接平衡树基本操作,这里不多赘述

但是这里主要说一些细节:

split(rt,mi-tag,a,b)

我们在每次执行S时,val<mi-tag的都要被切掉(之前写成<mi-tag-1)调了好久

还有就是读懂题目:

if(c=='I'&&x>=mi){insert(rt,x);}

如果他刚开始工资就少,那他必然是不干的

还有一个神仙细节是我写的FHQ-Treap貌似不能直接分裂出一个空树,所以当被割完之后的rt只有一个节点且其权值小于mi-tag时,将它重置然后将rt记为0:

if(t[rt].val<mi-tag&&t[rt].siz==1)
{
    t[rt]={0,0,0,0,0};
    rt=0;
}
tag=0;

然后这题基本就做完了

Code:

#include<bits/stdc++.h>
#include<ctime>
const int N=3e5+5;
using namespace std;
int n,m,mi,cnt,rt,tag,ans;
struct Tree{
	int val,pri,siz,ls,rs; 
}t[N<<2];
int new_(int val=0)
{
	t[++cnt]={val,rand(),1,0,0};
	return cnt;
}
void pushup(int x)
{
	t[x].siz=t[t[x].ls].siz+t[t[x].rs].siz+1;
}
void split(int x,int k,int &l,int &r)
{
	if(!x){l=r=0;return ;}
	if(k<=t[x].val)//in ls
	{
		r=x;
		split(t[x].ls,k,l,t[r].ls);
		pushup(r);
	}
	else//in rs
	{
		l=x;
		split(t[x].rs,k,t[l].rs,r);
		pushup(l);
	}
}
int merge(int x,int y)
{
	if(!x||!y)return x+y;
	if(t[x].pri<t[y].pri)
	{
		t[x].rs=merge(t[x].rs,y);
		pushup(x);
		return x;
	}
	else
	{
		t[y].ls=merge(x,t[y].ls);
		pushup(y);
		return y;
	}
}
int query_kth(int x,int k)
{
	if(k==t[t[x].ls].siz+1)return t[x].val;
	if(k<=t[t[x].ls].siz)return query_kth(t[x].ls,k);
	return query_kth(t[x].rs,k-(t[t[x].ls].siz+1));
}
void dfs(int x,int k)
{
	if(!x)return ;
	t[x].val+=k;
	dfs(t[x].ls,k);dfs(t[x].rs,k);
	pushup(x);
}
void upd_val(int &rt,int k)//-=k
{
	if(k<0)
	{
		tag+=k;
		int a=0,b=0;//val+tag<mi :val<mi-tag
		split(rt,mi-tag,a,b);//a[1,k-1] : b[k,inf]
		rt=b;	
		ans+=t[a].siz;
		dfs(rt,tag);
		pushup(rt);
		if(t[rt].val<mi-tag&&t[rt].siz==1)
		{
			t[rt]={0,0,0,0,0};
			rt=0;
		}
		tag=0;
	}
	else 
	{
		tag+=k;
		dfs(rt,tag);
		pushup(rt);
		tag=0;
	}
}
void insert(int &rt,int k)
{
	int a=0,b=0;
	split(rt,k,a,b);//a[1,k] : b[k+1,inf]
	rt=merge(merge(a,new_(k)),b);
	pushup(rt);
}
void work()
{
	cin>>n>>mi;
	char c;
	int x;
	for(int i=1;i<=n;i++)
	{
		std::cin>>c>>x;
		if(c=='I'&&x>=mi){insert(rt,x);}
		if(c=='A'){upd_val(rt,x);}
		if(c=='S'){upd_val(rt,-x);}
		if(c=='F')
		{
			int k=t[rt].siz-x+1; 
			printf("%d\n", k>0? query_kth(rt,k) : -1);
		}
	}
	printf("%d",cnt-t[rt].siz);
}
int main()
{
	//freopen("P1486_3.in","r",stdin);//freopen("P1486.out","w",stdout);
	srand(clock());
	work();
	return 0;
}
posted @ 2024-12-06 11:48  liuboom  阅读(81)  评论(0)    收藏  举报