1. 程式人生 > 實用技巧 >【題解】【bzoj 1503】【NOI2004】鬱悶的出納員

【題解】【bzoj 1503】【NOI2004】鬱悶的出納員

傳送門

前置芝士

總體思路

首先,I 和 F 看起來比較清真,應該隨便來個平衡樹或者線段樹之類的瞎維護一下就好。

主要是 A 和 S。

演算法 1:

暴力加。

演算法 2:

仿照線段樹,打 Tag.

我們維護一個 Tag 變數和一個 minwage 變數。

別問我為什麼是 wage,我也不知道。

I 操作的時候,要把給定的工資減去 Tag

F 操作的時候,找到之後加上 Tag 再輸出即可。

A 操作的時候,設輸入的第二個數為 delt,則 Tag+=delt,minwage-=delt; 即可。

S 操作同理 A,只不過要處理一下工資過低的人們大規模 sudo rm -rf / 加跑路的情況。

我們用無旋 treap 維護這個過程,除了大規模跑路的情況以外,所有操作都應該已經很簡單了。

至於刪除所有工資低於給定數的人,在無旋 treap 上其實也就很簡單了,把整棵樹 split 成兩顆(無旋 treap 基本操作),然後亂搞一下就好了(見程式碼)。

事實上囉嗦這麼半天感覺還不如直接上程式碼簡單。

奇怪的點:立刻離開公司的人不計入最後一行的答案。

程式碼:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e7+9;
struct node{
	int val;
	int rnd;
	int l,r;
	int sz;
}tree[N];
int n;
int tot,root;
int Tag,leavetot;
int minwage; 
int newnode(int x){
	tree[++tot]=(node){x,rand(),0,0,1};
	return tot;
}
void pushup(int x){
	tree[x].sz=tree[tree[x].l].sz+tree[tree[x].r].sz+1;
}
void split(int now,int k,int&l,int&r){
	if(!now){
		l=r=0;
		return;
	}
	if(tree[now].val<=k){
		l=now;
		split(tree[now].r,k,tree[now].r,r);
	}
	else{
		r=now;
		split(tree[now].l,k,l,tree[now].l);
	}
	pushup(now);
}
int merge(int x,int y){
	if(!x)return y;
	if(!y)return x;
	if(tree[x].rnd<tree[y].rnd){
		tree[x].r=merge(tree[x].r,y);
		return pushup(x),x;
	}
	else{
		tree[y].l=merge(x,tree[y].l);
		return pushup(y),y;
	}
}
void insert(int val){
	int x,y;
	split(root,val,x,y);
	root=merge(merge(x,newnode(val)),y);
}
int getval(int rk,int pos=root){
	while(1){
		int v=tree[tree[pos].l].sz;
		if(rk==v+1)return tree[pos].val;
		else if(rk<=v)pos=tree[pos].l;
		else rk-=v+1,pos=tree[pos].r;
	}
}
signed main(){
	srand(time(0));
	cin>>n>>minwage;
	while(n--){
		char op;cin>>op;
		if(op=='I'){
			int wage;cin>>wage;
			wage-=Tag;
			if(wage>=minwage)insert(wage);
		}
		else if(op=='A'){
			int delt;cin>>delt;
			Tag+=delt,minwage-=delt;
		}
		else if(op=='S'){
			int delt;cin>>delt;
			Tag-=delt,minwage+=delt;
			int x,y;
			split(root,minwage-1,x,y);
			leavetot+=tree[x].sz;
			root=y;
		}
		else{
			int k;cin>>k;
			if(k>tree[root].sz)cout<<-1<<endl;
			else cout<<getval(tree[root].sz-k+1)+Tag<<endl;
		}
	}cout<<leavetot<<endl;
	return 0;
}

不懂的歡迎留言 AWA.

Over.