[ABC379D] Home Garden
题意:
开始有一个空集,有 \(Q\) 次操作,每次有标识数 \(op\):
- 若 \(op\) 为 \(1\):为集合添加一个元素 \(0\)。
- 若 \(op\) 为 \(2\):输入 \(T\),为集合内所有元素增加 \(T\)。
- 若 \(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;
}
标签:10,Garden,题解,ll,元素,leq,add,ABC379D,op
From: https://www.cnblogs.com/anins/p/18537626