【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
*/

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Gene_INNOCENT

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值