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\);A
和S
命令的總條數不超過 \(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;
}