P4458 [BJOI2018]链上二次求和

显然线段树,修改操作就是白给,难点在于查询。

对于每一次查询:

\[ans = \sum_{i = l}^r \sum_{j = 1}^{n - i + 1} \sum_{k = j}^{j + i - 1} w_i \]

其中 \(w_i\) 表示点 \(i\) 的点权。

接下来我们开始 乱搞 这个式子(bushi

\(s_n = \sum\limits_{i = 1}^n w_i, ss_n = \sum\limits_{i = 1}^n s_i, sss_n = \sum\limits_{i = 1}^n ss_i\),即 \(s\)\(w\) 的前缀和,\(ss\)\(w\) 的二次前缀和(前缀和的前缀和),\(sss\)\(w\) 的三次前缀和。

\[\begin{aligned} ans &= \sum_{i = l}^r (\sum_{j = 1}^{n - i + 1} s_{j + i - 1} - \sum_{j = 1}^{n - i + 1} s_{j - 1}) \\ &= \sum_{i = l}^r (\sum_{j = i}^{n} s_{j} - \sum_{j = 0}^{n - i} s_j) \\ &= \sum_{i = l}^r (ss_n - ss_{i - 1} - ss_{n - i}) \\ &= (r - l + 1)ss_n - (sss_{r - 1} - sss_{l - 2}) - (sss_{n - l} - sss_{n - r - 1}) \end{aligned} \]

然后,我们考虑该如何用线段树来维护 \(s, ss, sss\)\(s\) 维护起来很简单,难点就在 \(ss\)\(sss\) 上,我们将其继续分解。

\[ss_n = \sum_{i = 1}^n \sum_{j = 1}^i w_j \]

考虑每个 \(w_j\) 出现的次数,易知为 \(n - j + 1\),故原式可写成:

\[\begin{aligned} ss_n &= \sum_{i = 1}^n (n - i + 1) w_i \\ &= \sum_{i = 1}^n ((n + 1)w_i - w_i \times i) \\ &= (n + 1) s_n - \sum_{i = 1}^n (w_i \times i) \end{aligned} \]

\(s1_n = \sum\limits_{i = 1}^n (w_i \times i)\),则有:

\[ss_n = (n + 1)s_n - s1_n \]

再来考虑 \(sss_n\)

\[\begin{aligned} sss_n &= \sum_{i = 1}^n \sum_{j = 1}^i \sum_{k = 1}^j w_k \\ \end{aligned} \]

关于这里为什么没有用 \(ss_n\) 最终的 \(ss_n = (n + 1)s_n - s1_n\) 来进行推导:
因为上式已经经过种种变形以适应线段树的维护,已经不适合再用来推导了,如果用它来推导 \(sss\) 的式子,难免会给自己增添思维上的难度(甚至推不出来)。

还是考虑每个 \(w_j\) 出现的次数:对于 \(k \le j \le i\)\(w_k\) 会被计算 \(1\) 次。

那么 \(w_k\) 出现的次数 \(t_k\) 就可以写成:

\[\begin{aligned} t_k &= \sum_{j = k}^n \sum_{i = j}^n\\ &= \sum_{j = k}^n (n - j + 1) \\ &= \sum_{j = 1}^{n - k + 1} j\\ &= \frac{(n - k + 2)(n - k + 1)}2 \end{aligned} \]

所以:

\[\begin{aligned} sss_n &= \sum_{i = 1}^n (w_i \times t_i) \\ &= \sum_{i = 1}^n (w_i \times \frac{(n - i + 2)(n - i + 1)}2) \\ &= \frac 1 2 \sum_{i = 1}^n w_i \times (i^2 - (2n + 3)i + (n + 2)(n + 1)) \\ &= \frac 1 2 \sum_{i = 1}^n (w_i \times i^2 - (2n + 3)(w_i \times i) + (n + 2)(n + 1)w_i) \\ &= \frac 1 2 (s2_n - (2n + 3)s1_n + (n + 2)(n + 1)s_n) \end{aligned} \]

其中 \(s2_n = \sum\limits_{i = 1}^n (w_i \times i^2)\)

现在,我们就可以通过维护 \(s, s1, s2\) 来维护 \(ss, sss\)

对于叶子结点:

\[si_{pos} = l \\ sii_{pos} = l^2 \\ s_{pos} = w_l \\ s1_{pos} = w_l \times si_{pos} \\ s2_{pos} = w_l \times sii_{pos} \]

\(pushup\) 时:

\[si_{pos} = si_{ls} + si_{rs} \\ sii_{pos} = sii_{ls} + sii_{rs} \\ s_{pos} = s_{ls} + s_{rs} \\ s1_{pos} = s1_{ls} + s1_{rs} \\ s2_{pos} = s2_{ls} + s2_{rs} \]

\(pushdown\) 时(以左子举例,右子同理):

\[lazy_{ls} = lazy_{ls} + lazy_{pos} \\ s_{ls} = s_{ls} + lazy_{pos} \times len(ls) \\ s1_{ls} = s1_{ls} + lazy_{pos} \times si_{ls} \\ s2_{ls} = s2_{ls} + lazy_{pos} \times sii_{ls} \]

记得取模,以及 \(sii\) 可能会爆 \(int\),要开 \(long~long\)


\(Code\)

#include <bits/stdc++.h>

#define MAXN 200100
#define MOD 1000000007
#define lson pos << 1
#define rson pos << 1 | 1

using namespace std;

typedef long long ll;

const int INV2 = 5e8 + 4;

int n, m;
ll w[MAXN];

struct Seg {
    ll s[3], si, sii, lazy;
} t[MAXN << 2];

template<typename _T>
inline void read(_T &_x) {
    _x = 0;
    _T _f = 1;
    char _ch = getchar();
    while (_ch < '0' || '9' < _ch) {
        if (_ch == '-') _f = -1;
        _ch = getchar();
    }
    while ('0' <= _ch && _ch <= '9') {
        _x = (_x << 3) + (_x << 1) + (_ch & 15);
        _ch = getchar();
    }
    _x *= _f;
}

template<typename _T>
inline void write(_T _x) {
    if (_x < 0) {
        putchar('-');
        _x = -_x;
    }
    if (_x > 9) write(_x / 10);
    putchar('0' + _x % 10);
}

void pushup(int pos) {
    t[pos].s[0] = (t[lson].s[0] + t[rson].s[0]) % MOD;
    t[pos].s[1] = (t[lson].s[1] + t[rson].s[1]) % MOD;
    t[pos].s[2] = (t[lson].s[2] + t[rson].s[2]) % MOD;
}

void pushdown(int pos, ll len) {
    if (!t[pos].lazy) return;
    t[lson].lazy = (t[lson].lazy + t[pos].lazy) % MOD;
    t[rson].lazy = (t[rson].lazy + t[pos].lazy) % MOD;
    t[lson].s[0] = (t[lson].s[0] + (len - (len >> 1)) * t[pos].lazy) % MOD;
    t[lson].s[1] = (t[lson].s[1] + t[pos].lazy * t[lson].si) % MOD;
    t[lson].s[2] = (t[lson].s[2] + t[pos].lazy * t[lson].sii) % MOD;
    t[rson].s[0] = (t[rson].s[0] + (len >> 1) * t[pos].lazy) % MOD;
    t[rson].s[1] = (t[rson].s[1] + t[pos].lazy * t[rson].si) % MOD;
    t[rson].s[2] = (t[rson].s[2] + t[pos].lazy * t[rson].sii) % MOD;
    t[pos].lazy = 0;
}

void build(int pos, int l, int r) {
    if (l == r) {
        t[pos].si = l;
        t[pos].sii = 1ll * l * l % MOD;
        t[pos].s[0] = w[l];
        t[pos].s[1] = w[l] * t[pos].si % MOD;
        t[pos].s[2] = w[l] * t[pos].sii % MOD;
        return;
    }
    int mid = (l + r) >> 1;
    build(lson, l, mid), build(rson, mid + 1, r);
    pushup(pos);
    t[pos].si = (t[lson].si + t[rson].si) % MOD;
    t[pos].sii = (t[lson].sii + t[rson].sii) % MOD;
}

void update(int pos, int l, int r, int x, int y, ll c) {
    if (x <= l && r <= y) {
        t[pos].lazy = (t[pos].lazy + c) % MOD;
        t[pos].s[0] = (t[pos].s[0] + c * (r - l + 1)) % MOD;
        t[pos].s[1] = (t[pos].s[1] + t[pos].si * c) % MOD;
        t[pos].s[2] = (t[pos].s[2] + t[pos].sii * c) % MOD;
        return;
    }
    pushdown(pos, r - l + 1);
    int mid = (l + r) >> 1;
    if (x <= mid) update(lson, l, mid, x, y, c);
    if (y > mid) update(rson, mid + 1, r, x, y, c);
    pushup(pos); 
}

ll query(int pos, int l, int r, int x, int y, int id) {
    if (x <= l && r <= y) return t[pos].s[id];
    pushdown(pos, r - l + 1);
    ll res = 0;
    int mid = (l + r) >> 1;
    if (x <= mid) res = (res + query(lson, l, mid, x, y, id)) % MOD;
    if (y > mid) res = (res + query(rson, mid + 1, r, x, y, id)) % MOD;
    return res; 
}

ll getss() {
    return ((n + 1) * query(1, 1, n, 1, n, 0) % MOD - query(1, 1, n, 1, n, 1) + MOD) % MOD;
}

ll getsss(ll x) {
    if (x <= 0) return 0;
    ll tmp = (query(1, 1, n, 1, x, 2) - ((x << 1) + 3) * query(1, 1, n, 1, x, 1) % MOD + query(1, 1, n, 1, x, 0) * (x + 2) % MOD * (x + 1) % MOD) % MOD;
    return tmp * INV2 % MOD;
}

int main() {
    read(n), read(m);
    for (int i = 1; i <= n; i++) read(w[i]);
    build(1, 1, n);
    while (m--) {
        int op, x, y;
        ll c;
        read(op), read(x), read(y);
        if (x > y) swap(x, y);
        if (op == 1) {
            read(c);
            update(1, 1, n, x, y, c);
        } else {
            ll tmp = ((y - x + 1) * getss() - (getsss(y - 1) - getsss(x - 2)) - (getsss(n - x) - getsss(n - y - 1))) % MOD;
            write((tmp + MOD) % MOD);
            putchar('\n');
        }
    }
    return 0;
}

作者:chy12321

出处:https://www.cnblogs.com/chy12321/p/16729909.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   Chy12321  阅读(62)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示
more_horiz
keyboard_arrow_up dark_mode palette
选择主题