BZOJ 1503: [NOI2004]郁闷的出纳员

一眼看感觉有点麻烦,splay树上的权值需要全部修改,还需要一个个去删除,多看了一会后发现每个操作都挺方便的。
操作1:直接insert即可。
操作2:注意到A,S的个数和是在100以内的,所以暴力把splay树上的权值都增加即可。
操作3:暴力把splay树上的权值减少,减少以后可能会有一部分点由于权值小于minn而被删掉,对于这个删除操作,可以这样处理:找到第一个大于等于minn的节点,把这个节点移到根上去,然后删除根的左儿子即可。
操作4:这个应该大家都会了。
注意读入的时候不要作死用cin,我cin>>opt被BZOJ卡,一开始还不知道为什么超时呢。
#include <bits/stdc++.h>
using namespace std;
const int N=1e6+5;
int n,minn,x,add,a,b,ans,sum,ncnt,root;
int ch[N][2],cnt[N],size[N],fa[N],val[N];

inline int chk(int x)
{
	return ch[fa[x]][1]==x;
}

inline void pushup(int x)
{
	size[x]=size[ch[x][0]]+size[ch[x][1]]+cnt[x];
}

inline void rotate(int x)
{
	int y=fa[x],z=fa[y],k=chk(x),w=ch[x][k^1];
	ch[y][k]=w; fa[w]=y;
	ch[z][chk(y)]=x; fa[x]=z;
	ch[x][k^1]=y; fa[y]=x;
	pushup(y); pushup(x);
}

inline void splay(int x,int goal)
{
	while (fa[x]!=goal)
	{
		int y=fa[x],z=fa[y];
		if (z!=goal)
		{
			if (chk(x)==chk(y)) rotate(y);
			else rotate(x);
		}
		rotate(x);
	}
	if (!goal) root=x;
}

inline void insert(int x)
{
	int cur=root,p=0;
	while (cur && x!=val[cur])
	{
		p=cur;
		cur=ch[cur][x>val[cur]];
	}
	if (cur) cnt[cur]++;
	else
	{
		cur=++ncnt;
		if (p) ch[p][x>val[p]]=cur;
		ch[cur][0]=ch[cur][1]=0;
		fa[cur]=p; val[cur]=x;
		cnt[cur]=size[cur]=1;	
	}
	splay(cur,0);
}

inline int find(int x)
{
	int cur=root;
	while (ch[cur][x>val[cur]] && x!=val[cur]) cur=ch[cur][x>val[cur]];
	splay(cur,0);
}

inline int kth(int x)
{
	int cur=root;
	while (true)
	{
		if (ch[cur][1] && x<=size[ch[cur][1]]) cur=ch[cur][1];
		else if (x>size[ch[cur][1]]+cnt[cur])
		{
			x-=size[ch[cur][1]]+cnt[cur];
			cur=ch[cur][0];
		}
		else return cur;
	}
}

inline int nxt2(int x)
{
	find(x);
	if (val[root]>=x) return root;
	int cur=ch[root][1];
	while (ch[cur][0]) cur=ch[cur][0];
	return cur;
}

inline char read_char()
{
	char ch=getchar();
	while (ch!='I' && ch!='A' && ch!='S' && ch!='F') ch=getchar();
	return ch;
}

int main(){
	insert(1e8);
	scanf("%d%d",&n,&minn);
	for (register int i=1; i<=n; ++i)
	{
		char opt;
		opt=read_char();
		scanf("%d",&x);
		if (opt=='I')
		{
			if (x<minn) continue;
			insert(x);
		}
		if (opt=='A')
		{
			for (register int j=1; j<=ncnt; ++j) val[j]+=x;
		}
		if (opt=='S')
		{
			for (register int j=1; j<=ncnt; ++j) val[j]-=x; 
			
			int rt;
			rt=nxt2(minn);
			splay(rt,0);
			ans+=size[ch[root][0]];
			ch[root][0]=0;
			pushup(root);
		}
		if (opt=='F')
		{
			if (size[root]-1<x) printf("%d\n",-1);
			else 
			{
				printf("%d\n",val[kth(x+1)]);
			}
		}
	}
	printf("%d\n",ans);
return 0;	
}
对于2,3两个操作还可以通过O(1)修改来得到:
改动一下insert里的值,nxt2的值和输出的值就行。至于为什么insert(x-add),nxt2(minn-add),val[kth(x+1)]+add,自己模拟一下数据即可。
int main(){
	insert(1e8);
	scanf("%d%d",&n,&minn);
	for (register int i=1; i<=n; ++i)
	{
		char opt;
		opt=read_char();
		scanf("%d",&x);
		if (opt=='I')
		{
			if (x<minn) continue;
			insert(x-add);
		}
		if (opt=='A')
		{
			add+=x;
		}
		if (opt=='S')
		{
			add-=x;
			
			int rt;
			rt=nxt2(minn-add);
			splay(rt,0);
			ans+=size[ch[root][0]];
			ch[root][0]=0;
			pushup(root);
		}
		if (opt=='F')
		{
			if (size[root]-1<x) printf("%d\n",-1);
			else 
			{
				printf("%d\n",val[kth(x+1)]+add);
			}
		}
	}
	printf("%d\n",ans);
return 0;	
}
发布了64 篇原创文章 · 获赞 29 · 访问量 698

猜你喜欢

转载自blog.csdn.net/Dove_xyh/article/details/103546621