很多要注意的细节,很蛋疼。
关于修改操作,可以很巧妙地用一个变量
a
d
d
add
add全局记录。(累计所有修改的总和)
当插入一个数的
k
k
k时候(如果
k
k
k小于
l
o
w
low
low就不管,因为直接退出的员工是不计入总数的),维持它与其它数的相对大小即可。于是平衡树上插入一个节点
(
k
−
a
d
d
)
(k-add)
(k−add)。因为其它数相当于一开始就比它多了
a
d
d
add
add。整体加的时候,给
a
d
d
add
add加就行了。因为题目保证了给的是非负整数。整体减的时候,先给
a
d
d
add
add减。此时的相对工资底线就是
(
l
o
w
−
a
d
d
)
(low-add)
(low−add)(理由同上)。
然后就是删除操作:把小于 ( l o w − a d d ) (low-add) (low−add)的数全部删掉。我们可以把第一个大于等于(low-add) 的数转到根。然后把根的左子树全部删掉。删完之后再更新一下 r o o t root root。
于是有一个问题:
如果这个底线很高,而工资又减得很多,那么会出现
v
a
l
[
r
o
o
t
]
<
(
l
o
w
−
a
d
d
)
val[root]<(low-add)
val[root]<(low−add)的情况。这时候
r
o
o
t
root
root也是需要删掉的。
解决办法:我们一开始插入一个无穷大的数 ∞ \infty ∞。当出现这种情况时, ∞ \infty ∞就会成为根节点。如果不是这种情况, ∞ \infty ∞节点也一定在最右下角,只会影响根节点右儿子中节点的 s i z e size size大小。不会对当前统计答案造成影响。
然后看查询操作。
由于插入了
∞
\infty
∞,节点总数多了一个。那么把询问第
k
k
k大转化为询问第
k
+
1
k+1
k+1大就好。记得要加上
a
d
d
add
add。一个数进来的时候减去了
a
d
d
add
add,
a
d
d
add
add又经过了一系列修改,这个数出去的时候加上了
a
d
d
add
add,就恰好还原了这个数。对于
−
1
-1
−1,随手判断一下就好。
然而这样会出锅。经过漫长的观察,发现:
插入一个10,插入一个20,这时候根节点就是后面这个点。
然后再插入一个20。这时候由于它是根节点,那么不会有
s
p
l
a
y
splay
splay或
r
o
t
a
t
e
rotate
rotate操作,于是插入后面这个20的时候,我们只会增加根节点的
c
n
t
cnt
cnt,
s
i
z
e
size
size却得不到修改。然后现在询问就会出现问题。
解决办法1:用一个 p e o p l e people people变量,记录人数。 i n s e r t insert insert的时候加1,删除的时候减去删除子树的 s i z e size size。这样可以保证询问第 k k k大的这个 k k k没有问题。而在询问的时候,只会去看当前节点左右儿子的 s i z e size size和当前节点的 c n t cnt cnt。根节点的 s i z e size size没有更新是不会影响的。
解决办法2: i n s e r t insert insert完一个节点之后, p u s h u p pushup pushup这个节点。
数据结构还是要深入理解算法的运行过程才能用的溜啊
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
const int oo=1<<30;
int tot=0,root=0,fa[maxn],son[maxn][2],size[maxn],cnt[maxn],val[maxn];
int n,low,k,ans=0,add=0;char op[3];
inline int read(){
int x=0;char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x;
}
inline int get(int x){return x==son[fa[x]][1];}
inline void pushup(int x){size[x]=size[son[x][1]]+size[son[x][0]]+cnt[x];}
inline void clear(int x){son[x][0]=son[x][1]=fa[x]=size[x]=cnt[x]=0;}
inline void rotate(int x){
int y=fa[x],z=fa[y],k=get(x),l=son[x][k^1];
son[y][k]=l;if(l)fa[l]=y;
if(z)son[z][get(y)]=x;fa[x]=z;
fa[y]=x,son[x][k^1]=y;
pushup(y),pushup(x);
}
inline void splay(int x,int goal=0){
while(fa[x]!=goal){
int y=fa[x],z=fa[y];
if(z!=goal) rotate((get(x)==get(y))?(y):(x));
rotate(x);
}if(!goal) root=x;
}
inline void insert(int x){
int now=root,f=0;
while(now&&x!=val[now]) f=now,now=son[now][val[now]<x];
if(now) cnt[now]++;
else{
now=++tot;if(f) son[f][val[f]<x]=now;
val[now]=x,fa[now]=f,cnt[now]=size[now]=1;
}splay(now),pushup(now);
}
inline void find(int x){
int now=root;
while(son[now][val[now]<x]&&x!=val[now])
now=son[now][val[now]<x];
splay(now);
}
inline int kth(int k){
int now=root;
while(1){
if(k<=size[son[now][1]]) now=son[now][1];
else if(k<=size[son[now][1]]+cnt[now]) return val[now];
else k-=size[son[now][1]]+cnt[now],now=son[now][0];
}
}
//把大于等于x的第一个数转到根。
inline void suffix(int x){
find(x);
if(val[root]<x){
int now=son[root][1];
while(son[now][0]) now=son[now][0];
splay(now);
}
}
int main(){
n=read(),low=read(),insert(oo);
while(n--){
scanf("%s",op),k=read();
if(op[0]=='A') add+=k;
if(op[0]=='S'){
add-=k,suffix(low-add),ans+=size[son[root][0]];
clear(son[root][0]),son[root][0]=0,pushup(root);
}
if(op[0]=='F') printf("%d\n",(k<size[root])?(kth(k+1)+add):(-1));
if(op[0]=='I'&&k>=low) insert(k-add);
}printf("%d\n",ans);
}
用 p e o p l e people people记录
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
const int oo=1<<30;
int tot=0,root=0,fa[maxn],son[maxn][2],size[maxn],cnt[maxn],val[maxn];
int people,add=0,n,low,k,ans=0;char op[3];
inline int read(){
int x=0;char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return x;
}
inline int get(int x){return x==son[fa[x]][1];}
inline void pushup(int x){size[x]=size[son[x][1]]+size[son[x][0]]+cnt[x];}
inline void clear(int x){son[x][0]=son[x][1]=fa[x]=size[x]=cnt[x]=0;}
inline void rotate(int x){
int y=fa[x],z=fa[y],k=get(x),l=son[x][k^1];
son[y][k]=l;if(l)fa[l]=y;
if(z)son[z][get(y)]=x;fa[x]=z;
fa[y]=x,son[x][k^1]=y;
pushup(y),pushup(x);
}
inline void splay(int x,int goal=0){
while(fa[x]!=goal){
int y=fa[x],z=fa[y];
if(z!=goal) rotate((get(x)==get(y))?(y):(x));
rotate(x);
}if(!goal) root=x;
}
inline void insert(int x){
int now=root,f=0;
while(now&&x!=val[now]) f=now,now=son[now][val[now]<x];
if(now) cnt[now]++;
else{
now=++tot;if(f) son[f][val[f]<x]=now;
val[now]=x,fa[now]=f,cnt[now]=size[now]=1;
}splay(now);
}
inline void find(int x){
int now=root;
while(son[now][val[now]<x]&&x!=val[now])
now=son[now][val[now]<x];
splay(now);
}
inline int kth(int k){
int now=root;
while(1){
if(k<=size[son[now][1]]) now=son[now][1];
else if(k<=size[son[now][1]]+cnt[now]) return val[now];
else k-=size[son[now][1]]+cnt[now],now=son[now][0];
}
}
//把大于等于x的第一个数转到根。
inline void suffix(int x){
find(x);
if(val[root]<x){
int now=son[root][1];
while(son[now][0]) now=son[now][0];
splay(now);
}
}
int main(){
n=read(),low=read(),insert(oo);
while(n--){
scanf("%s",op),k=read();
if(op[0]=='A') add+=k;
if(op[0]=='S'){
add-=k,suffix(low-add),ans+=size[son[root][0]],people-=size[son[root][0]];
clear(son[root][0]),son[root][0]=0,pushup(root);
}
if(op[0]=='F') printf("%d\n",(k<=people)?(kth(k+1)+add):(-1));
if(op[0]=='I'&&k>=low) insert(k-add),people++;
}printf("%d\n",ans);
}