郁闷的出纳员 HYSBZ - 1503 权值线段树

题解

使用权值线段树解决问题
记录一个mdf表示先前工资的变动 增加工资时增加mdf减少工资时减少mdf并将工资小于min - mdf - 1的数值标记清除 每次查询第k大先判断是否有足够人数
使用一个tot记录加入人数 最后使用tot-线段树1节点的人数即为离开人数

AC代码

#include <stdio.h>
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int INF = 0x3f3f3f3f;
const int MAXN = 4e5 + 10;

struct node
{
	int l, r, v, lzy;
	void Lazy()
	{
		v = 0;
		lzy = 1; //区间清零
	}
}tre[MAXN * 4];
inline void PushUp(int x)
{
	tre[x].v = tre[x << 1].v + tre[x << 1 | 1].v;
}
inline void PushDown(int x)
{
	if (tre[x].lzy)
	{
		tre[x << 1].Lazy(), tre[x << 1 | 1].Lazy();
		tre[x].lzy = 0;
	}
}
void Build(int x, int l, int r)
{
	tre[x].l = l, tre[x].r = r, tre[x].v = tre[x].lzy = 0;
	if (l == r)
		return;
	int m = l + r >> 1;
	Build(x << 1, l, m);
	Build(x << 1 | 1, m + 1, r);
}
void Update(int x, int v, int k) //将值v增加k个 单点修改
{
	int l = tre[x].l, r = tre[x].r;
	if (l == r)
	{
		tre[x].v += k;
		return;
	}
	PushDown(x);
	int m = l + r >> 1;
	if (v <= m)
		Update(x << 1, v, k);
	else
		Update(x << 1 | 1, v, k);
	PushUp(x);
}
void Erase(int x, int pl, int pr) //将值[pl, pr]清零 区间修改lzy标记
{
	int l = tre[x].l, r = tre[x].r;
	if (pl <= l && r <= pr)
	{
		tre[x].Lazy(); //因为lazy不能记录清空数量
		return;
	}
	PushDown(x);
	int m = l + r >> 1;
	if (m >= pl)
		Erase(x << 1, pl, pr);
	if (m < pr)
		Erase(x << 1 | 1, pl, pr);
	PushUp(x);
}
int Kth(int x, int k) //返回全局倒数第k大的值 其它功能见非区间清零
{
	int l = tre[x].l, r = tre[x].r;
	if (l == r) 
		return l; //L
	int m = l + r >> 1;
	if (tre[x << 1 | 1].v >= k) //倒数正数切换左右侧
		return Kth(x << 1 | 1, k);
	return Kth(x << 1, k - tre[x << 1 | 1].v);
}
int main()
{
#ifdef LOCAL
	freopen("C:/input.txt", "r", stdin);
#endif
	int N, mi;
	cin >> N >> mi;
	Build(1, -2e5, 2e5);
	int mdf = 0, tot = 0, v; //整体修改值 人数
	char op[10];
	for (int i = 1; i <= N; i++)
	{
		scanf("%s%d", op, &v);
		if (op[0] == 'I' && v >= mi)
			Update(1, v - mdf, 1), tot++;
		else if (op[0] == 'A')
			mdf += v;
		else if (op[0] == 'S')
		{
			mdf -= v;
			if (mi - mdf - 1 >= -2e5)
				Erase(1, -2e5, mi - mdf - 1);
		}
		else if (op[0] == 'F')
		{
			if (v > tre[1].v) //超过总人数
				printf("-1\n");
			else
				printf("%d\n", Kth(1, v) + mdf);
		}
	}
	cout << tot - tre[1].v << endl;

	return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值