Luogu P5073

题面 简述:全局加、区间最大子段和。

做这题之前请确保你会:线段树凸包闵可夫斯基和

如果没有修改或只有单点修改,那就是 经典问题这题现在似乎也成经典问题了):线段树节点上维护区间和 sum、最大前缀和 ls、最大后缀和 rs、最大子段和 ans

现在有了修改,仍然考虑维护这些东西。考虑全局加上一个数 k 对这些量的影响:sum 直接加上 k 乘以区间长度,但剩下的三个就比较困难了,因为这和它们取到最大值时的长度有关。

因此我们不能只维护最大值,我们需要维护 ls(i),rs(i),ans(i) 分别表示长为 i 的前缀和、后缀和、最大子段和的值。此时全局加上一个数 k 相当于 ls(i)ls(i)+ki,也就是加上一个一次函数,剩下的两个同理。我们求出这三个函数的凸包(上凸壳),然后 lower_bound 出斜率最接近 k​ 的那条线段就可以得到最大值。

对线段树的每个节点都维护这三个凸包,每次查询时的 k 就是到当前为止所有修改操作的 x 的和,这样单次查询是 O(log2n) 的。如果我们把所有询问按 k 排序,则凸包的最值点是单调的,此时我们只需要每个凸包维护一个指针,查询的时间复杂度均摊成了 O(mlogn)

我们发现这个线段树其实是静态的,即建好后就不用修改了,因此只考虑建树。

lsrs 都可以 O() 暴力求,总时间复杂度为 O(nlogn)。关键在于 ans 怎么求。

考虑从左右儿子合并,不难发现 ans(i+j)=max{rsl(i)+lsr(j)},其中 rsl 表示左儿子的 rslsr 表示右儿子的 ls,然后还要对两个儿子的 ansmax

我们发现第一步就是闵可夫斯基和,而第二步只需要将点集合并后重新跑一遍凸包。

这样即可做到 O(nlogn) 建树。

然而本题空间只有 128MB,存下每个节点所需的空间是 O(nlogn) 的,会爆。

那么我们考虑如何不把所有节点都存下来:在建树过程中维护当前区间需要处理的询问,求出当前节点后直接回答;两个儿子用完后直接销毁,这样任意时刻存着的总凸包大小都是 O(n)。虽然这样存询问就变成了 O(nlogn) 的空间,但是常数小了很多,可以过。

总时间复杂度 O((n+m)logn)

#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;
}
posted @   untitled0  阅读(16)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示