Luogu P5073
题面 简述:全局加、区间最大子段和。
如果没有修改或只有单点修改,那就是 经典问题(这题现在似乎也成经典问题了):线段树节点上维护区间和
现在有了修改,仍然考虑维护这些东西。考虑全局加上一个数
因此我们不能只维护最大值,我们需要维护 lower_bound
出斜率最接近
对线段树的每个节点都维护这三个凸包,每次查询时的
我们发现这个线段树其实是静态的,即建好后就不用修改了,因此只考虑建树。
考虑从左右儿子合并,不难发现
我们发现第一步就是闵可夫斯基和,而第二步只需要将点集合并后重新跑一遍凸包。
这样即可做到
然而本题空间只有 128MB,存下每个节点所需的空间是
那么我们考虑如何不把所有节点都存下来:在建树过程中维护当前区间需要处理的询问,求出当前节点后直接回答;两个儿子用完后直接销毁,这样任意时刻存着的总凸包大小都是
总时间复杂度
#include<bits/stdc++.h>
#define endl '\n'
#define F first
#define S second
// #define int ll
#define rep(i, s, e) for(int i = (s), i##E = (e); i <= i##E; ++i)
#define per(i, s, e) for(int i = (s), i##E = (e); i >= i##E; --i)
#define gmin(x, y) (x = min(x, y))
#define gmax(x, y) (x = max(x, y))
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double f128;
typedef pair<int, int> pii;
#ifndef ONLINE_JUDGE
#define debug(fmt, ...) fprintf(stderr, "[%d] " fmt "\n", __LINE__, ##__VA_ARGS__)
#else
#define debug(fmt, ...)
#endif
char gc() {
static char buf[1 << 20], *st, *ed;
if(st == ed) st = buf, ed = buf + fread(buf, 1, 1 << 20, stdin);
return st == ed ? EOF : *st++;
}
template<typename T>
void read(T &x) {
int flg = 1;
char c;
while(!isdigit(c = gc()) && c != '-');
if(c == '-') flg = -1, x = 0;
else x = c - '0';
while(isdigit(c = gc())) x = x * 10 + c - '0';
x *= flg;
}
template<typename T, typename ...Args>
void read(T &x, Args &...y) {
read(x); read(y...);
}
constexpr int N = 3e5 + 5, M = 6e5 + 5;
int n, m, Q, a[N];
struct Query {
int l, r;
ll add;
Query(int L = 0, int R = 0, ll A = 0):
l(L), r(R), add(A) {}
} qu[M];
vector<int> q;
void input() {
read(n, m);
rep(i, 1, n) read(a[i]);
ll tmp = 0;
rep(i, 1, m) {
int op, l, r;
read(op);
if(op == 1) read(l), tmp += l;
else {
read(l, r);
qu[++Q] = Query(l, r, tmp);
}
}
q.resize(Q);
rep(i, 0, Q - 1) q[i] = i + 1;
sort(q.begin(), q.end(), [](int &a, int &b) {
return qu[a].add < qu[b].add;
});
}
struct Vector {
ll x, y;
Vector(ll X = 0, ll Y = 0) noexcept : x(X), y(Y) {}
Vector operator+(const Vector &v) const {
return Vector(x + v.x, y + v.y);
}
Vector operator-(const Vector &v) const {
return Vector(x - v.x, y - v.y);
}
ll operator*(const Vector &v) const {
return x * v.y - y * v.x;
}
};
ll ls[N], rs[N];
struct node {
vector<Vector> ls, rs, hs;
};
void convex(vector<Vector> &p) {
static int stk[M], *top;
top = stk;
*++top = 0;
rep(i, 1, p.size() - 1) {
while(top != stk + 1 && (p[*top] - p[*(top - 1)]) * (p[i] - p[*top]) >= 0)
--top;
*++top = i;
}
vector<Vector> res(top - stk);
rep(i, 1, top - stk) res[i - 1] = p[stk[i]];
swap(p, res);
}
void make(node &nd, int l, int r) {
nd.ls.resize(r - l + 2);
nd.rs.resize(r - l + 2);
nd.ls[0] = nd.rs[0] = Vector(0, 0);
ls[l] = a[l], rs[l] = a[r];
rep(i, 1, r - l) {
ls[l + i] = ls[l + i - 1] + a[l + i];
rs[l + i] = rs[l + i - 1] + a[r - i];
}
rep(i, 0, r - l) {
nd.ls[i + 1] = Vector(i + 1, ls[l + i]);
nd.rs[i + 1] = Vector(i + 1, rs[l + i]);
}
convex(nd.ls), convex(nd.rs);
}
void minkowski(vector<Vector> &a, vector<Vector> &b, vector<Vector> &res) {
static Vector A[M], B[M];
rep(i, 1, a.size() - 1) A[i] = a[i] - a[i - 1];
rep(i, 1, b.size() - 1) B[i] = b[i] - b[i - 1];
res.resize(a.size() + b.size() - 1);
res[0] = Vector(0, 0);
int i = 1, j = 1, tot = 0;
while(i < a.size() && j < b.size()) {
if(A[i] * B[j] <= 0) res[tot + 1] = res[tot] + A[i++];
else res[tot + 1] = res[tot] + B[j++];
++tot;
}
while(i < a.size()) res[tot + 1] = res[tot] + A[i++], ++tot;
while(j < b.size()) res[tot + 1] = res[tot] + B[j++], ++tot;
// a.clear(); a.shrink_to_fit();
// b.clear(); b.shrink_to_fit();
}
void pushup(node &nd, node &lc, node &rc) {
minkowski(lc.rs, rc.ls, nd.hs);
rep(i, 1, lc.hs.size() - 1) nd.hs.emplace_back(lc.hs[i]);
rep(i, 1, rc.hs.size() - 1) nd.hs.emplace_back(rc.hs[i]);
// lc.hs.clear(); lc.hs.shrink_to_fit();
// rc.hs.clear(); rc.hs.shrink_to_fit();
sort(nd.hs.begin(), nd.hs.end(), [](const Vector &a, const Vector &b) {
return a.x < b.x;
});
convex(nd.hs);
}
struct Data {
ll sum, ls, rs, hs;
} ans[M];
void merge(Data &a, Data b) {
a = {
a.sum + b.sum,
max(a.ls, a.sum + b.ls),
max(b.rs, b.sum + a.rs),
max({a.hs, b.hs, a.rs + b.ls})
};
}
void build(node &nd, int l, int r, vector<int> &q) {
// debug("called build(%d, %d, %d, %d)", p, l, r, (int)q.size());
make(nd, l, r);
// if(q.empty()) return;
if(l == r) {
nd.hs.emplace_back(0, 0);
nd.hs.emplace_back(1, a[l]);
for(auto id : q) {
auto [ql, qr, add] = qu[id];
if(ql <= l && r <= qr) {
ll sum = a[l] + add;
ll ls, rs, hs;
ls = rs = hs = max(a[l] + add, 0ll);
merge(ans[id], {sum, ls, rs, hs});
}
}
return;
}
ll tmp = ls[r];
int mid = (l + r) >> 1;
vector<int> here, down;
for(auto &i : q) {
if(qu[i].l <= l && r <= qu[i].r) here.emplace_back(i);
else if(qu[i].l <= r && qu[i].r >= l) down.emplace_back(i);
}
node lc, rc;
build(lc, l, mid, down);
build(rc, mid + 1, r, down);
pushup(nd, lc, rc);
int lp = 0, rp = 0, hp = 0;
for(auto id : here) {
auto [ql, qr, add] = qu[id];
while(lp + 1 < nd.ls.size() && nd.ls[lp].x * add + nd.ls[lp].y
<= nd.ls[lp + 1].x * add + nd.ls[lp + 1].y)
++lp;
while(rp + 1 < nd.rs.size() && nd.rs[rp].x * add + nd.rs[rp].y
<= nd.rs[rp + 1].x * add + nd.rs[rp + 1].y)
++rp;
while(hp + 1 < nd.hs.size() && nd.hs[hp].x * add + nd.hs[hp].y
<= nd.hs[hp + 1].x * add + nd.hs[hp + 1].y)
++hp;
ll sum = tmp + (r - l + 1) * add;
ll lmx = nd.ls[lp].x * add + nd.ls[lp].y;
ll rmx = nd.rs[rp].x * add + nd.rs[rp].y;
ll hmx = nd.hs[hp].x * add + nd.hs[hp].y;
merge(ans[id], {sum, lmx, rmx, hmx});
}
}
signed main() {
input();
node nd;
build(nd, 1, n, q);
rep(i, 1, Q) cout << ans[i].hs << endl;
return 0;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战