【BZOJ 1503】郁闷的出纳员【权值线段树】

前期知识:

      权值线段树本质 —— 每个节点存取的是一个区间内的信息,即在 [l,r] 这个区间内的信息,而普通的线段树可能是维护区间内的最大值,sum和之类的信息。

      当数据范围比较大,但是数据个数不多的时候,可以使用离散化实现权值线段树。

      权值线段树可用于求取全局第 k 大之类的信息。

题意:

      建立员工档案,要求支持动态加入、删除员工,统计工资第 k 大的员工,员工工资最多100000。

思路:

      建立权值线段树,统计每一个区间内的人数,记一个add变量,用来记录现在对工资的加减总和,然后用权值线段树即可动态删点、插点、统计区间第 K 大。

      在删点的时候,可以用lazy进行标记,标记一段区间num为0,对时间进行优化(不优化也能过),详见代码。

代码:

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#define rep(i,a,b) for(int i = a; i <= b; i++)
using namespace std;
const int N = 401000;
const int base = 200020;

struct Tree{
	int l,r,num,lazy;
}t[N*4];
char s[10];
int minn,add;

void build(int p,int l,int r)
{
	t[p].l = l, t[p].r = r, t[p].num = 0, t[p].lazy = 0;
	if(t[p].l == t[p].r) return;
	int mid = (l+r) >> 1;
	build(p*2,l,mid);
	build(p*2+1,mid+1,r);
}

void calc(int p)
{
	if(t[p].lazy == 0) return;
	else{
		t[p].lazy = 0;
		t[p*2].lazy = 1, t[p*2+1].lazy = 1;
		t[p*2].num = t[p*2+1].num = 0;
	}
}

void insert(int p,int x)
{
	if(t[p].l == t[p].r){
		if(t[p].l == x)
			t[p].num++;
		return;
	} 
	int mid = (t[p].l+t[p].r)>>1;
	calc(p);
	if(x <= mid) insert(p*2,x);
	else insert(p*2+1,x);
	t[p].num = t[p*2].num+t[p*2+1].num;
}

void update(int p,int x)
{
	if(t[p].r < x){
		t[p].num = 0, t[p].lazy = 1;
		return;
	}
	if(t[p].l == t[p].r){
		if(t[p].r < x) t[p].num = 0;//n-=t[p].num, t[p].num = 0;
		return;
	}
	int mid = (t[p].l+t[p].r)>>1;
	calc(p);
	if(x <= mid) update(p*2,x);
	else{
		update(p*2,x);
		update(p*2+1,x);
	} 
	t[p].num = t[p*2].num + t[p*2+1].num;
}

int ask(int p,int x)
{
	if(t[p].l == t[p].r) return t[p].l;
	int mid = (t[p].l + t[p].r)>>1;
	calc(p);
	if(t[p*2].num >= x) return ask(p*2,x);
	else return ask(p*2+1,x-t[p*2].num);
}

int main()
{
	int kk,x,sum = 0;
	scanf("%d%d",&kk,&minn);
	build(1,0,400100);
	rep(i,1,kk)
	{
	//	printf("n:%d,add:%d\n",t[1].num,add);
		scanf("%s%d",s,&x);
		if(s[0] == 'I')
		{
			if(x < minn) continue;
			sum++;
			insert(1,x-add+base);
		}
		else if(s[0] == 'A')
			add += x;
		else if(s[0] == 'S')
		{
			add -= x;
			update(1,minn-add+base);
		}
		else{
			if(x > t[1].num) printf("-1\n");
			else printf("%d\n",ask(1,t[1].num-x+1)+add-base);
		}
	}
	printf("%d\n",sum-t[1].num);
	return 0;
}

/*
18 9
A 671
S 211
S 492
A 560
I 446
I 116
I 945
I 441
A 59
S 315
I 58
S 839
A 873
F 725
A 109
I 521
F 998
A 938
*/

猜你喜欢

转载自blog.csdn.net/qq_41552508/article/details/83116131