P2221 [HAOI2012]高速公路

P2221 [HAOI2012]高速公路

洛谷:P2221 [HAOI2012]高速公路

Solution

注意两个方向都可以走QWQ

直接表达期望:

\[Ex(l, r) = \frac{\sum\limits_{i = l}^{r}\sum\limits_{j = l}^{r}d(i, j)}{\binom{r - l + 1}{2}} \]

发现问题在上面的式子,我们记为 \(S\)。很自然地想到对每一条线段统计贡献,可以得到

\[S = \sum\limits_{i = l}^{r - 1}v_i(i - l + 1)(r - i) \]

接下来的思路是,把式子拆出只与 \(v_i\) 相关的成分,而使其他部分可以 \(O(1)\) 得出。

\[\begin{aligned} S &= \sum\limits_{i = l}^{r - 1}v_i(ir - lr + r - i^2 + il - i) \\ &= \sum\limits_{i = l}^{r - 1}v_i[(r - lr) + (l + r - 1)i - i^2] \end{aligned} \]

\(s1 = \sum\limits_{i = l}^{r - 1}v_i, s2 = \sum\limits_{i = l}^{r - 1}i\times v_i, s3 = \sum\limits_{i = l}^{r - 1}i^2 \times v_i\)。则

\[S = (r - lr)s1 + (l + r - 1)s2 - s3 \]

于是用数据结构将三个和式分别维护。

#include<bits/stdc++.h>
#define ls(x) x << 1
#define rs(x) x << 1 | 1
#define LL long long
using namespace std;
const int N = 1e5 + 5;
int n, Q;
LL GCD(LL a, LL b)
{
    if(!b) return a;
    return GCD(b, a % b);
}
struct Nagisa
{
    LL add;
};
struct SegTree
{
    int l, r;
    LL sum1, sum2, sum3;
    LL sum4, sum5; // sum{i}, sum{i^2}
    Nagisa nag;
}tr[4 * N];
void pushup(SegTree &u, SegTree L, SegTree R)
{
    u.sum1 = L.sum1 + R.sum1;
    u.sum2 = L.sum2 + R.sum2;
    u.sum3 = L.sum3 + R.sum3;
}
void build(int p, int l, int r)
{
    tr[p].l = l;
    tr[p].r = r;
    if(l == r)
    {
        tr[p].sum1 = tr[p].sum2 = tr[p].sum3 = 0;
        tr[p].sum4 = l;
        tr[p].sum5 = 1LL * l * l;
        return;
    }
    int mid = l + (r - l) / 2;
    build(ls(p), l, mid);
    build(rs(p), mid + 1, r);
    pushup(tr[p], tr[ls(p)], tr[rs(p)]);
    tr[p].sum4 = tr[ls(p)].sum4 + tr[rs(p)].sum4;
    tr[p].sum5 = tr[ls(p)].sum5 + tr[rs(p)].sum5;
}
void cal(SegTree &u, int v)
{
    u.sum1 += 1LL * (u.r - u.l + 1) * v;
    u.sum2 += u.sum4 * v;
    u.sum3 += u.sum5 * v;
    u.nag.add += v;
}
void pushdown(int p)
{
    if(tr[p].nag.add)
    {
        cal(tr[ls(p)], tr[p].nag.add);
        cal(tr[rs(p)], tr[p].nag.add);
        tr[p].nag.add = 0;
    }
}
void modify(int p, int l, int r, int v)
{
    if(tr[p].l >= l && tr[p].r <= r)
    {
        cal(tr[p], v);
        return;
    }
    pushdown(p);
    int mid = tr[p].l + (tr[p].r - tr[p].l) / 2;
    if(mid >= l) modify(ls(p), l, r, v);
    if(mid < r) modify(rs(p), l, r, v);
    pushup(tr[p], tr[ls(p)], tr[rs(p)]);
}
SegTree query(int p, int l, int r)
{
    if(tr[p].l >= l && tr[p].r <= r)
        return tr[p];
    pushdown(p);
    int mid = tr[p].l + (tr[p].r - tr[p].l) / 2;
    if(mid < l) return query(rs(p), l, r);
    else if(mid >= r) return query(ls(p), l, r);
    else
    {
        SegTree L = query(ls(p), l, r);
        SegTree R = query(rs(p), l, r);
        SegTree res;
        pushup(res, L, R);
        return res;
    }
}
int main()
{
    scanf("%d %d", &n, &Q);
    build(1, 1, n - 1);
    while(Q--)
    {
        char op;
        int l, r, v;
        scanf(" %c", &op);
        if(op == 'C')
        {
            scanf("%d %d %d", &l, &r, &v);
            if(l == r) continue;
            modify(1, l, r - 1, v);
        }
        else
        {
            scanf("%d %d", &l, &r);
            SegTree res = query(1, l, r - 1);
            LL x = 1LL * (l + r - 1) * res.sum2 - 1LL * r * (l - 1) * res.sum1 - res.sum3;
            LL y = 1LL * (r - l + 1) * (r - l) / 2;
            LL d = GCD(x, y);
            printf("%lld/%lld\n", x / d, y / d);
        }
    }
    return 0;
}
posted @ 2023-01-29 16:41  Schucking_Sattin  阅读(26)  评论(0)    收藏  举报