题面
Problem Link
有(n)个盒子(m)个操作, 每个盒子有一个上限。每次的操作是区间加减一个数且超过上限的不算低于(0)的不算。求最后的结果。
题解
考虑这样一个搞法, 扫描线扫序列, 然后区间修改变单点, 用一个时间上的线段树维护每个时间的修改的时间后缀和, 那么考虑求答案。
可以在线段树上二分出最靠后的(mx - mn > c)的点, 如果顶了上下界, 这样就会二分出倒数第二次的顶上界时间, 讨论一下就可以得到答案。如果没有顶上下界的话直接使用(mx - mn)即可得到答案。
#include "candies.h"
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
using vint = vector<int>;
ll S;
int n, m;
const int N = 2e5 + 10;
vector<pair<int, int> > Q[N];
struct Node {
Node *ls, *rs;
int l, r;
ll mn, mx, tag;
Node() {}
Node(int _l, int _r) : l(_l), r(_r), mn(0), mx(0) , tag(0) { ls = rs = NULL; }
void upd() {
mn = min(ls -> mn, rs -> mn);
mx = max(ls -> mx, rs -> mx);
}
void pushtag(ll v) {
mn += v; mx += v; tag += v;
}
void pushdown() {
if(tag) {
ls -> pushtag(tag);
rs -> pushtag(tag);
tag = 0;
}
}
void modify(int L, int R, ll v) {
if(L <= l && r <= R) {
this -> pushtag(v);
return ;
}
this -> pushdown();
int mid = (l + r) >> 1;
if(L <= mid) ls -> modify(L, R, v);
if(R > mid) rs -> modify(L, R, v);
this -> upd();
}
int query(int L, int R, int C, ll mnv, ll mxv) {
if(l == r) {
if(this -> mx > S) return S - mnv;
return C - (mxv - S);
}
this -> pushdown();
int mid = (l + r) >> 1;
ll tmin = min(mnv, rs -> mn);
ll tmax = max(mxv, rs -> mx);
if(tmax - tmin <= C) return ls -> query(L, R, C, tmin, tmax);
else return rs -> query(L, R, C, mnv, mxv);
}
} ;
Node *rt;
Node *build(int l, int r) {
Node * x = new Node(l, r);
if(l == r) return x;
int mid = (l + r) >> 1;
x -> ls = build(l, mid);
x -> rs = build(mid + 1, r);
x -> upd();
return x;
}
vint distribute_candies(vint c, vint l, vint r, vint v) {
n = c.size();
m = l.size();
vint ans; ans.resize(n);
for(int i = 0; i < m; i ++)
Q[l[i]].push_back({i, v[i]}),
Q[r[i] + 1].push_back({i, -v[i]});
rt = build(0, m);
for(int i = 0; i < n; i ++) {
for(auto p : Q[i]) S += p.second, rt -> modify(p.first + 1, m, p.second);
if (rt -> mx - rt -> mn <= c[i])
ans[i] = S - rt -> mn;
else
ans[i] = rt -> query(0, m, c[i], 0x3f3f3f3f3f3f3f3fLL, -0x3f3f3f3f3f3f3f3fLL);
}
return ans;
}