P5609 [Ynoi2013] 对数据结构的爱 题解

我永远喜欢数据结构。

显然 $l,r$ 的答案 $\in\{\sum\limits_{i=l}^ra_i-zp|z\in\mathbf{N}\}$,考虑求出 $z$。

线段树。每个节点上维护 $k_i$ 表示 $z=i$ 时,进入该节点时的当前答案最小值,

显然,若进入该节点时当前答案为 $g$,则 $k_z\le g<k_{z+1}$,容易二分求出 $z$。

考虑怎么算 $k_i$。对在左孩子里减去 $x$ 个 $p$,在右孩子里减去 $y$ 个 $p$ 的情况,有

$$ \begin{cases} k_{x+y}\ge\text{lson}.k_x\\ k_{x+y}+\sum\limits_{i\in\text{lson}}a_i-xp\ge\text{rson}.k_y\\ \text{lson}.k_{x+1}-1+\sum\limits_{i\in\text{lson}}a_i-xp\ge\text{rson}.k_y \end{cases} $$

即,$k_{x+y}=\min\limits_{\text{lson}.k_{x+1}-1+\sum\limits_{i\in\text{lson}}a_i-xp\ge\text{rson}.k_y}\{\max\{\text{lson}.k_x,\text{rson}.k_y-\sum\limits_{i\in\text{lson}}a_i+xp\}\}$。

显然 $k$ 有单调性,双指针维护条件 $\text{lson}.k_{x+1}-1+\sum\limits_{i\in\text{lson}}a_i-xp\ge\text{rson}.k_y$ 即可。

#include <cstdio>
#include <algorithm>
using namespace std;
int n, m;
long long P;
struct T
{
    T *l, *r;
    int s, t;
    long long v, *k;
    T(int s, int t) : s(s), t(t), k(new long long[t - s + 3])
    {
        k[0] = -1e18;
        for (int i = 1; i <= t - s + 2; ++i)
            k[i] = 1e18;
    }
    void u()
    {
        v = l->v + r->v;
        for (int x = 0, y = 0; x <= l->t - l->s + 1; ++x)
        {
            y -= y == r->t - r->s + 2;
            for (; y <= r->t - r->s + 1; ++y)
            {
                if (l->k[x + 1] - 1 + l->v - x * P < r->k[y])
                {
                    --y;
                    break;
                }
                k[x + y] = min(k[x + y], max(l->k[x], r->k[y] - l->v + x * P));
            }
        }
    }
} * r;
void B(int s, int t, T *&p)
{
    p = new T(s, t);
    if (s == t)
        return (void)(scanf("%lld", &p->v), p->k[1] = P - p->v);
    int m = p->s + p->t >> 1;
    B(s, m, p->l);
    B(m + 1, t, p->r);
    p->u();
}
long long Q(int l, int r, long long g, T *p)
{
    if (l <= p->s && p->t <= r)
        return g + p->v - P * (upper_bound(p->k, p->k + p->t - p->s + 3, g) - p->k - 1);
    int m = p->s + p->t >> 1;
    if (l <= m && r > m)
        return Q(l, r, Q(l, r, g, p->l), p->r);
    if (l <= m)
        return Q(l, r, g, p->l);
    if (r > m)
        return Q(l, r, g, p->r);
}
int main()
{
    scanf("%d%d%lld", &n, &m, &P);
    B(1, n, r);
    for (int i = 0, x, y; i < m; ++i)
        scanf("%d%d", &x, &y), printf("%lld\n", Q(x, y, 0, r));
    return 0;
}
posted @ 2023-03-07 21:01  5k_sync_closer  阅读(4)  评论(0编辑  收藏  举报  来源