洛谷:P1486 [NOI2004]郁闷的出纳员

views
Word count: 923 (~4 mins to read) Last updated:

原题地址:https://www.luogu.org/problemnew/show/P1486

题目简述

一个序列a,初始为空。
随时插入一个数,查询第k大,全体加,全体减。
但是如果任何数在任何时刻低于给定的下界MIN,则立即移除出序列。


思路

插入,查询第k大,容易发现是BST题。于是上Treap。
全体加全体减暴力加肯定不行,考虑用变量delta储存变化情况。全体加n就是delta+=n(n为负就是减)
于是每个数实际的值是:树里储存该数的值+delta ——①
减了之后可能会有数低于下界,查找最小的数判断是不是小于MIN,是的话删除,重复直到不再小于MIN。
注意新插入的数不应该受之前的加减影响,所以将一个数字num插入树中时,如果直接把num插入树中,就变成num+delta了。
实际应该插入的是num-delta,这样结合上文①,现在这个数实际的值就是num本身了。
提供一个指针实现的Treap,不推荐使用其实,调的时候快把我搞吐血。


代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <ctime>
#include <cctype>
using namespace std;
const int MAXN=200010;
struct Node
{
int d,rnd,size;
Node *ch[2],*pa;
}pool[MAXN],*root;
int tot=0;
Node *newnode(int d)
{
Node *p=&pool[++tot];
p->d=d;p->rnd=rand();p->size=1;
p->ch[0]=p->ch[1]=NULL;
return p;
}
inline int size(Node *p)
{
return p?p->size:0;
}
void update(Node *p)
{
p->size=size(p->ch[0])+size(p->ch[1])+1;
}
void rotate(Node *p,int t)
{
Node *pa=p->pa,*gp=pa->pa,*son=p->ch[t^1];
pa->ch[t]=son;
if(son)son->pa=pa;
if(gp)gp->ch[pa==gp->ch[1]]=p;
p->pa=gp;
p->ch[t^1]=pa;
pa->pa=p;
if(pa==root)root=p;
update(pa);
update(p);
}
void treap(Node *p)
{
while(true)
{
if(p==root || p->rnd >= p->pa->rnd)break;
rotate(p,p==p->pa->ch[1]);
}
}
void insert(Node *r,Node *p)
{
if(!r)
{
root=p;
return;
}
int f=(p->d >= r->d);
if(!r->ch[f])
{
r->ch[f]=p;
p->pa=r;
}
else
insert(r->ch[f],p);
update(r);
}
Node* find(Node *r,int x)
{
if(x<=size(r->ch[0]))return find(r->ch[0],x);
if(x==size(r->ch[0])+1)return r;
return find(r->ch[1],x-size(r->ch[0])-1);
}
void del(Node *p)
{
if(!p->ch[0] && !p->ch[1])
{
if(p==root)
{
root=NULL;
return;
}
Node *pa=p->pa;
if(pa)
pa->ch[p==pa->ch[1]]=NULL;
while(p!=root)
update(p=p->pa);
return;
}
if(p->ch[0] && p->ch[1])
{
int f=(p->ch[1]->rnd < p->ch[0]->rnd);
rotate(p->ch[f],f);
}
else
{
int f=1;
if(p->ch[0])f=0;
rotate(p->ch[f],f);
}
del(p);
}
int kth(Node *r,int x)
{
if(x<=size(r->ch[0]))return kth(r->ch[0],x);
if(x==size(r->ch[0])+1)return r->d;
return kth(r->ch[1],x-size(r->ch[0])-1);
}
char getUpper()
{
char c;
while(c=getchar())
if(isupper(c))
return c;
}
int main()
{
srand((int)time(NULL));
int Q,Min,x;
char op;
scanf("%d%d",&Q,&Min);
int ans=0,delta=0;
while(Q--)
{
op=getUpper();
scanf("%d",&x);
switch(op)
{
case 'I':
{
if(x<Min)break;
Node *p=newnode(x-delta);
insert(root,p);
treap(p);
break;
}
case 'A':
delta+=x;
break;
case 'S':
{
delta-=x;
int xtq=size(root);
for(int i=1;i<=xtq;i++)
if(kth(root,1)+delta<Min)
{
del(find(root,1));
ans++;
}
else break;
break;
}
case 'F':
if(x>size(root))printf("-1\n");
else printf("%d\n",kth(root,size(root)-x+1)+delta);
break;
}
}
printf("%d\n",ans);
return 0;
}