Loading

题解:[ABC379D] Home Garden

[ABC379D] Home Garden

题意:

开始有一个空集,有 \(Q\) 次操作,每次有标识数 \(op\)

  1. \(op\)\(1\):为集合添加一个元素 \(0\)
  2. \(op\)\(2\):输入 \(T\),为集合内所有元素增加 \(T\)
  3. \(op\)\(3\):输入 \(H\),删除集合内不小于 \(H\) 的元素,并输出删除元素个数。

数据范围:\(1 \leq Q \leq 2 \times 10^{5}\)\(1 \leq T,H \leq 10^{9}\)

思路:

因为题中有多元素修改操作,所以我使用线段树统计区间加来实现。

设变量 \(l\)\(r\) 表示未被删除的元素集合的在线段树内的位置。

我们发现 \(1 \leq Q \leq 2 \times 10^{5}\),直接用线段树根节点表示 \(1\)\(2 \times 10^{5}\) 这个区间。

  • 对于操作 \(1\):直接令 \(r\) 加一即可。
  • 对于操作 \(2\):线段树对区间 \(l\)\(r\)\(T\)
  • 对于操作 \(3\):我们发现未删集合在在 \(l\)\(r\) 内内单调不增,那么考虑二分找出在 \(l\)\(r\) 内第一个小于 \(H\) 的元素位置,设它为 \(L\),那么答案就是 \(L-l\),输出后将 \(l\) 赋值为 \(L\) 即可更新未删集合。

代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
struct tree {
	#define ls (k<<1)
	#define rs ((k<<1)|1)
	#define mid ((l+r)>>1)
	ll add[800005];
	void push_down(ll k) {
		if(!add[k]) return;
		add[ls]+=add[k];
		add[rs]+=add[k];
		add[k]=0;
	}
	void solve(ll k, ll l, ll r, ll L, ll R, ll op) {
		if(L<=l&&r<=R) {
			add[k]+=op;
			return;
		}
		push_down(k);
		if(L<=mid) solve(ls, l, mid, L, R, op);
		if(R>mid) solve(rs, mid+1, r, L, R, op);
	}
	ll query(ll k, ll l, ll r, ll op) {
		if(l==r) return add[k];
		push_down(k);
		if(op<=mid) return query(ls, l, mid, op);
		else return query(rs, mid+1, r, op);
	}
}sg;
int main() {
	ll Q, T, H, l=1, r=0, op;
	cin >> Q;
	while(Q--) {
		cin >> op;
		if(op==1) r++;
		else if(op==2) {
			cin >> T;
			if(l<=r) sg.solve(1, 1, 200000, l, r, T); //注意判断是否是空集 
		} else if(op==3) {
			cin >> H;
			//使用二分找出l-r内第一个小于H的元素位置,若都不小于H那么l将会更新为r+1合题意 
			ll L=l, R=r, Mid;
			while(L<=R) {
				Mid=(L+R)/2;
				if(sg.query(1, 1, 200000, Mid)>=H) L=Mid+1;
				else R=Mid-1;
			}
			cout << L-l << endl;
			l=L; //更新未删集合 
		}
	}
	return 0;
}
posted @ 2024-11-10 08:34  Anins  阅读(15)  评论(0)    收藏  举报