【題解】【bzoj 1503】【NOI2004】鬱悶的出納員
阿新 • • 發佈:2020-08-23
總體思路
首先,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.