[郁闷的出纳员] ( Splay树 )
From: http://www.lydsy.com/JudgeOnline/problem.php?id=1503
Input
第一行有两个非负整数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是一个正整数。
在初始时,可以认为公司里一个员工也没有。
Output
输出文件的行数为F命令的条数加一。
对于每条F命令,你的程序要输出一行,仅包含一个整数,为当前工资第k多的员工所拿的工资数,如果k大于目前员工的数目,则输出-1。
输出文件的最后一行包含一个整数,为离开公司的员工的总数。
Sample Input
9 10 I 60 I 70 S 50 F 2 I 30 S 15 A 5 F 1 F 2
Sample Output
10 20 -1 2
Hint
【约定】
l I命令的条数不超过100000
l A命令和S命令的总条数不超过100
l F命令的条数不超过100000
l 每次工资调整的调整量不超过1000
l 新员工的工资不超过100000
思路: SPLAY
设d0为最小工资, v是树中节点的代价。
对于S操作, 增量为m, 那么 v - m < d0的都要从树中删除,即 v < m + d0的节点被移除。如何移除这些满足条件的节点?找到不小于m+d0的最大节点x, 把x旋转到根部, 然后把x的右子女作为根,复杂度O(lbn)。
/************************************************************** Problem: 1503 User: leezy Language: C++ Result: Accepted Time:728 ms Memory:3224 kb ****************************************************************/ #include <iostream> #include <stdio.h> #include <string.h> #include <cmath> using namespace std; #define N 100005 #define M 1000000 #define INF 0x7fffffff struct { int v,l,r,p,sz; }t[N]; int cnt,root; int alloc(int v){ t[++cnt].v = v; t[cnt].sz = 1; t[cnt].l = t[cnt].r = t[cnt].p = 0; return cnt; } void rot_l(int x){ int y = t[x].r; t[x].r = t[y].l; t[t[y].l].p = x; t[y].p = t[x].p; if ( !t[x].p ) root = y; else if ( t[t[x].p].l == x ) t[t[x].p].l = y; else t[t[x].p].r = y; t[y].l = x; t[x].p = y; t[y].sz = t[x].sz; t[x].sz = t[t[x].l].sz + t[t[x].r].sz + 1; } void rot_r(int x){ int y = t[x].l; t[x].l = t[y].r; t[t[y].r].p = x; t[y].p = t[x].p; if ( !t[x].p ) root = y; else if ( t[t[x].p].l == x ) t[t[x].p].l = y; else t[t[x].p].r = y; t[y].r = x; t[x].p = y; t[y].sz = t[x].sz; t[x].sz = t[t[x].l].sz + t[t[x].r].sz + 1; } void splay(int x){ while ( t[x].p ){ int p1 = t[x].p; if ( t[p1].l == x ){ if ( !t[p1].p ) rot_r(p1); else { int p2 = t[p1].p; if ( t[p2].l == p1 ) rot_r(p2), rot_r(p1); else rot_r(p1), rot_l(p2); } } else { if ( !t[p1].p ) rot_l(p1); else { int p2 = t[p1].p; if ( t[p2].r == p1 ) rot_l(p2), rot_l(p1); else rot_l(p1), rot_r(p2); } } } } void ins(int v){ int x = root, y = 0; while ( x ) { y = x; ++t[x].sz; if ( t[x].v < v ) x = t[x].r; else x = t[x].l; } int k = alloc(v); t[k].p = y; if ( !y ) root = k; else if ( t[y].v < v ) t[y].r = k; else t[y].l = k; splay(k); } int find(int x, int k){ int r = t[t[x].l].sz + 1; if ( r == k ) return t[x].v; if ( r < k ) return find(t[x].r, k-r); return find(t[x].l, k); /*x = root; while ( x ) { int r = t[t[x].l].sz + 1; if ( r == k ) return t[x].v; if ( r < k ) k-=r, x=t[x].r; else x=t[x].l; } return -1;*/ } int adjust(int d){ int x = root, k = 0; while ( x ) { if ( t[x].v < d ) k = x, x = t[x].r; else x = t[x].l; } if ( k != 0 ){ splay(k); if ( t[k].v < d ){ root = t[k].r; t[root].p = 0; return t[t[k].l].sz+1; } } return 0; } int main() { //const char path[] = "D:\\Project\\AlgorithmExam\\test.txt"; //freopen(path, "r+", stdin); int n, m, d0; while ( scanf("%d%d", &n, &d0) != EOF){ t[0].l=t[0].r=t[0].v=t[0].p=t[0].sz=cnt = root = 0; char ch[5]; int num = 0, ADD = 0; for ( int i = 0; i < n ; ++i ){ scanf("%s%d", ch, &m); if ( ch[0] == 'I' ){ if ( m >= d0 ) ins(m-ADD); } else if ( ch[0] == 'A' ){ ADD += m; } else if ( ch[0] == 'S' ){ ADD -= m; num += adjust( -ADD + d0 ); } else if ( ch[0] == 'F' ){ if ( !root || t[root].sz < m ) printf("-1\n"); else { printf("%d\n", find(root,t[root].sz-m+1)+ADD); } } } printf("%d\n", num); } return 0; }
也用跳表做了一遍, 关键在于分段维护每段的大小。对于第i层, 划分为(a0,a1](a1,a2]...(ak,an], 对任意节点ai, sz[ai] = #(区间(ai-1, ai])。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/************************************************************** Problem: 1503 User: leezy Language: C++ Result: Accepted Time:856 ms Memory:39492 kb ****************************************************************/ #include <stdio.h> #include <string.h> #include <time.h> #include <stdlib.h> #include <functional> #define N 50015 #define M 1000000 #define INF 0x6fffffff #define MAX_LEVEL 32 #define NIL (0) #define min(a,b) ((a)<(b))?(a):(b) struct SkipListNode{ int key, sz; int prev, next, up, down; } Node[N*MAX_LEVEL+N]; int List[MAX_LEVEL+1], Upd[MAX_LEVEL+1], tot[MAX_LEVEL+1], top, cnt; void Dump(){ int k = top; while ( k >= 0 ){ printf("Level[%d]: ", k); int p = List[k--]; while ( p != NIL ){ if ( Node[p].key == -INF ) printf("-INF[%d]-->", Node[p].sz); else printf("%d[%d]-->", Node[p].key, Node[p].sz); p = Node[p].next; } printf("\n"); } } void Init(int seed){ top = -1; cnt = 0; Node[0].key = INF; Node[0].sz = 0x0fffffff; srand(seed); } int NewNode(int key){ int cc = ++cnt; Node[cc].key = key; Node[cc].sz = 0; Node[cc].next = Node[cc].prev = Node[cc].up = Node[cc].down = NIL; return cc; } template<class Comp> int Find(int key, const Comp& comp){ if ( top < 0 ) return NIL; int k = top, p = List[top]; tot[top+1] = 0; while ( k >= 0 ){ tot[k] = tot[k+1]; for (int q = Node[p].next; comp(Node[q].key, key); ){ tot[k] += Node[q].sz; p = q, q = Node[p].next; } Upd[k--] = p; p = Node[p].down; } return (Node[Upd[0]].key == -INF)?(NIL):(tot[0]); } void Ins(int key){ //printf("Ins\n"); Find(key, std::less_equal<int>()); int k = 0; for (float r = 0; r < 0.5 && k < MAX_LEVEL; r=((float)rand())/RAND_MAX) ++k; for (; top + 1 < k; ){ int p = NewNode(-INF); if ( -1 == top ) List[++top] = p; else { int q = List[top]; Node[q].up = p; Node[p].down = q; List[++top] = p; } tot[top] = 0; Upd[top] = p; } for (int i = 0, top2 = -1; i <= top; ++i ){ int q = Node[Upd[i]].next; if ( i < k ){ int p = NewNode(key); Node[p].sz = tot[0] - tot[i] + 1; Node[q].sz = Node[q].sz - Node[p].sz; Node[p].next = q; Node[q].prev = p; Node[Upd[i]].next = p; Node[p].prev = Upd[i]; if ( -1 == top2 ) top2 = p; else { Node[top2].up = p; Node[p].down = top2; top2 = p; } } Node[q].sz += 1; } //Dump(); } int Del(int key){ //printf("Del\n"); int r = Find(key, std::less<int>()); if ( NIL != r ){ for (int i = 0; i <= top; ++i){ int p = List[i]; int q = Node[Upd[i]].next; Node[q].sz = Node[q].sz - tot[0] + tot[i]; Node[p].next = q; Node[q].prev = p; } for (; top >= 0 && Node[Node[List[top]].next].key == INF; ) --top; //Dump(); } return r; } int Find_Kth(int k){ if ( top < 0 ) return -1; int top2 = top, p = List[top]; while ( top2 >= 0 ){ for (int q = Node[p].next; Node[q].sz < k; p = q, q = Node[p].next) k -= Node[q].sz; Upd[ top2-- ] = p; p = Node[p].down; } return (Node[Node[Upd[0]].next].sz != k)?(-1):(Node[Node[Upd[0]].next].key); } int main() { //const char path[] = "D:\\Project\\AlgorithmExam\\test.txt"; //freopen(path, "r+", stdin); int n,m, num; while ( scanf("%d%d", &n, &m) != EOF ){ Init(num+n+m); num = 0; int d = 0, v, t = 0; char op[5]; for ( int i = 0; i < n; ++i ){ scanf("%s%d", op, &v); if ( 'I' == op[0] ){ if ( v >= m ) Ins(v-d), ++t; } else if ( 'A' == op[0] ){ d += v; } else if ( 'S' == op[0] ){ d -= v; num += Del(m-d); } else if ( 'F' == op[0] ){ int dd = Find_Kth(t-num-v+1); if ( -1 == dd ) printf("-1\n"); else printf("%d\n", dd+d); } } printf("%d\n", num); } return 0; }