[BZOJ1503]NOI2004 郁闷的出纳员|splay

这题方法应该有很多,什么树状数组线段树都能搞。。

用splay的话就是维护工资序列咯,但是每次修改都逐个修改的话肯定会T掉,注意到修改是整体修改,所以只要维护一个相对的修改值,然后修改的时候要考虑上这个相对值。。增加工资显然不会使员工离开,所以只要在减少工资的时候check一下就好了。。删除的时候要注意维护size。。

#include<iostream>
#include<cstdio>
#include<memory.h>
using namespace std;
const int inf=999999999,maxn=100005;
int n,minp,i,ans=0,del=0,k,root=0,nnode=0,c[maxn][2],pre[maxn],size[maxn],same[maxn],data[maxn];
char cc;
void update(int x){size[x]=size[c[x][0]]+size[c[x][1]]+same[x];}
void newnode(int &x,int fa,int num)
{
	x=++nnode;
	pre[x]=fa;data[x]=num;
	size[x]=same[x]=1;
	c[x][0]=c[x][1]=0;
}
void rot(int x,int kind)
{
	int y=pre[x];int z=pre[y];
	c[y][!kind]=c[x][kind];pre[c[x][kind]]=y;
	c[x][kind]=y;pre[y]=x;
	pre[x]=z;
	if (z) c[z][c[z][1]==y]=x;
	update(y);update(x);if (z) update(z);
}
void splay(int x,int goal)
{
	int y,z,kind;
	while (pre[x]!=goal)
	{
		if (pre[pre[x]]==goal) rot(x,c[pre[x]][0]==x);
		else
		{
			y=pre[x];z=pre[y];kind=c[z][0]==y;
			if (c[y][!kind]==x) rot(y,kind);else rot(x,!kind);
			rot(x,kind);
		}
	}
	if (goal==0) root=x;
}
void insert(int k)
{
	int get=root;
	if (!root) newnode(root,0,k);
	else
	{
		if (data[get]==k) same[get]++,size[get]++;
		else
		{
			while (c[get][data[get]<k])
			{
				get=c[get][data[get]<k];
				if (data[get]==k) 
				{
					same[get]++,splay(get,0);
					return;
				}
			}
			newnode(c[get][data[get]<k],get,k);
			splay(c[get][data[get]<k],0);
		}
	}
}
int findkth(int x,int k)
{
	if (size[c[x][0]]>=k) return findkth(c[x][0],k);
	else if (size[c[x][0]]+same[x]>=k) return data[x];
	else return findkth(c[x][1],k-size[c[x][0]]-same[x]);
}
void check(int x)
{
	if (!x) return;
	if (data[x]+del<minp)
	{
		ans+=size[c[x][0]]+same[x];
		pre[c[x][1]]=pre[x];c[pre[x]][c[pre[x]][1]==x]=c[x][1];
		if (pre[x]==0) root=c[x][1];
		int t=pre[x];
		while (t)
		{
			update(t);
			t=pre[t];
		}
		check(c[x][1]);
	}
	else check(c[x][0]);
}
void print(int x)
{
	printf("xu:%d num:%d lc:%d rc:%d size:%d same:%d\n",x,data[x],c[x][0],c[x][1],size[x],same[x]);
	if (c[x][0]) print(c[x][0]);if (c[x][1]) print(c[x][1]);
}
int main()
{
	freopen("cny.in","r",stdin);
	scanf("%d%d ",&n,&minp);
	size[0]=same[0]=data[0]=pre[0]=0;
	for (i=1;i<=n;i++)
	{
		scanf("%c%d ",&cc,&k);
		if (cc=='I'&&k>=minp) insert(k-del);
		if (cc=='A') del+=k;
		if (cc=='S') del-=k,check(root);
		if (cc=='F') if (size[root]>=k) printf("%d\n",findkth(root,size[root]+1-k)+del); else printf("-1\n");
	}
	printf("%d",ans);
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值