bzoj1503 郁闷的出纳员

传送门

很多要注意的细节,很蛋疼。
关于修改操作,可以很巧妙地用一个变量 a d d add add全局记录。(累计所有修改的总和)
当插入一个数的 k k k时候(如果 k k k小于 l o w low low就不管,因为直接退出的员工是不计入总数的),维持它与其它数的相对大小即可。于是平衡树上插入一个节点 ( k − a d d ) (k-add) (kadd)。因为其它数相当于一开始就比它多了 a d d add add。整体加的时候,给 a d d add add加就行了。因为题目保证了给的是非负整数。整体减的时候,先给 a d d add add减。此时的相对工资底线就是 ( l o w − a d d ) (low-add) (lowadd)(理由同上)。

然后就是删除操作:把小于 ( l o w − a d d ) (low-add) (lowadd)的数全部删掉。我们可以把第一个大于等于(low-add) 的数转到根。然后把根的左子树全部删掉。删完之后再更新一下 r o o t root root

于是有一个问题:
如果这个底线很高,而工资又减得很多,那么会出现 v a l [ r o o t ] &lt; ( l o w − a d d ) val[root]&lt;(low-add) val[root]<(lowadd)的情况。这时候 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);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值