【NOI2004】郁闷的出纳员(Treap)

题目描述请见原题。

分析

看完题目就觉得是个裸的数据结构题。由于最近才学了Treap的基本操作,就顺便当练手。

首先需要会Treap的基本操作。

第一类操作,由于这个题对员工编号没有要求,所以直接插入一个即可。

第二、三类,可以设一个delt,直接对delt加减即可。由于删除操作不会超过100次,而员工人数又不会超过100000,所以每次减工资后,直接暴力枚举删除即可。

第四类常规操作了。

容易出错的地方:

1,插入时,先判断是不是小于MIN,小于就continue。然后减去一个delt之后再插入,便于操作。

2,删除时,建议做一个后序DFS,也就是先检查儿子,再检查自己。因为旋转操作对父亲是没有影响的。每次一定要从root开始删除,因为整个树的大小在改变。

3,输出时,记得加上一个delt。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cstdlib>

using namespace std;

const int MAXN=100005;

int val[MAXN];
int ch[MAXN][2];
int cnt[MAXN];
int size[MAXN];
int prior[MAXN];
int rt=0,np=0,MIN,N,delt=0,tot=0,leave=0;

void update(int now) {size[now] = size[ch[now][0]] + size[ch[now][1]] + cnt[now];}

void rotate(int &now,int dir)
{
	int son = ch[now][dir^1];
	ch[now][dir^1] = ch[son][dir];
	ch[son][dir] = now;
	update(now);
	update(now = son);
}

void insert(int &now, int num)
{
	if(!now)
	{
		now = ++np;
		cnt[now] = size[now] = 1;
		val[now] = num;
		prior[now] = rand();
		return;
	}
	size[now]++;
	if(val[now] == num) {cnt[now]++; return;}
	int d = num < val[now];
	insert(ch[now][d], num);
	if(prior[ch[now][d]] < prior[now]) rotate(now, d^1);
}

void remove(int &now, int num)
{
	if(!now) return;
	if(val[now] == num)
	{
		if(cnt[now] > 1) {cnt[now]--; size[now]--; return;}
		if(!ch[now][0] || !ch[now][1]) {now = ch[now][0] + ch[now][1]; return;}
		int d = prior[ch[now][1]] < prior[ch[now][0]];
		rotate(now, d^1); remove(now, num);
		return;
	}
	
	size[now]--;
	int d = num < val[now];
	remove(ch[now][d], num);
}

void check(int now)
{
	if(ch[now][0]) check(ch[now][0]);
	if(ch[now][1]) check(ch[now][1]);
	if(val[now] + delt < MIN)
	{
		int t=cnt[now];
		while(t--) { tot--;leave++; remove(rt, val[now]); }
	}
}

int kth(int now, int k)
{
	while(now)
	{
		if(k <= size[ch[now][0]]) now = ch[now][0];
		else if(k > size[ch[now][0]] + cnt[now]) k -= size[ch[now][0]] + cnt[now], now = ch[now][1];
		else return val[now];
	}
}

int main()
{
	scanf("%d%d", &N, &MIN);
	
	char op;
	int x;
	
	while(N--)
	{
		scanf("%s%d", &op, &x);
		if(op == 'I') {if(x<MIN) continue; tot++; insert(rt, x-delt);}
		else if(op == 'A') delt += x;
		else if(op == 'S') {delt -= x; check(rt);}
		else {if(tot<x) printf("-1\n"); else printf("%d\n", kth(rt, x) + delt);}
	}
	printf("%d",leave);
	
	return 0;
}

猜你喜欢

转载自blog.csdn.net/WWWengine/article/details/81358970