243. 一个简单的整数问题2

给定一个长度为 N 的数列 A,以及 M 条指令,每条指令可能是以下两种之一:

  1. C l r d,表示把 A[l],A[l+1],…,A[r]都加上 d。
  2. Q l r,表示询问数列中第 l∼r 个数的和。

对于每个询问,输出一个整数表示答案。

输入格式

第一行两个整数 N,M。

第二行 N 个整数 A[i]。

接下来 M行表示 M 条指令,每条指令的格式如题目描述所示。

输出格式

对于每个询问,输出一个整数表示答案。

每个答案占一行。

数据范围

1≤N,M≤105,
|d|≤100000,
|A[i]|≤109

输入样例:
10 5
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
Q 2 4
输出样例:
4
55
9
15
代码:
/*
由于modify直接修改了一些区间上面的add与sum,导致递归路径上的父节点全部都需要重新算一遍sum,所以递归结尾需要加一个pushup,

同时递归路径上如果存在带有懒标记的区间,则区间结尾的pushup会用两个子区间的返回值直接覆盖该区间的sum,但是该区间的懒标记add值还没有加到子区间上面去,也就是说懒标记的附加值就会被直接覆盖掉。
这种pushup会直接消灭懒标记,所以modify递归路径上必须全部消灭懒标记,即每一层递归都要先pushdown。

这里可以看到pushup和pushdown是一个组合,递归中每一层pushup保证了递归树的叶节点的改变对父节点的影响能够及时修正,每一层的pushdown都能保证本次的递归路径上一定不存在懒标记,保证本次递归路径上的sum赋值全部为正确值。

所以尽管查询操作并没有新增懒标记且保证递归路径上没有懒标记导致我们不需要pushup,但当我们在query的时候在每一层返回sum之前加一次pushup上述的一套组合依然能保证查询结果一定是正确的,

所以可以从这一套组合的性质出发直接 背下来,在简单运用线段树模板时候直接modify和query都用这一套,就省事很多了。
*/
#include<bits/stdc++.h>
using namespace std;

#define x first
#define y second
#define int long long
#define endl '\n'
#define IOS ios::sync_with_stdio(false); cin.tie(0);cout.tie(0);
typedef unsigned long long ull;
typedef pair<int, int> PII;

int lowbit(int x) { return x & (- x); }
int  gcd(int a, int b) { return b ? gcd(b, a % b) : a; }
const int mod = 1e9 + 7;
const int N = 100010;

int n, m;
int w[N];
struct Node
{
    int l, r;
    int sum, add;
} tr[N * 4];


void pushup(int u)
{
    tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
}

void pushdown(int u)
{
    auto& root = tr[u], &left = tr[u << 1], &right = tr[u << 1 | 1];

    if (root.add)
    {
        left.add += root.add, left.sum += (left.r - left.l + 1) * root.add;
        right.add += root.add, right.sum += (right.r - right.l + 1) * root.add;
        root.add = 0;
    }
}


void build(int u, int l, int r)
{
    if (l == r) tr[u] = {l, r, w[r], 0};
    else
    {
        tr[u] = {l, r};
        int mid = l + r >> 1;
        build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
        pushup(u);
    }
}

void modify(int u , int l, int r, int d)
{
    if (tr[u].l >= l && tr[u].r <= r)
    {
        tr[u].sum += (tr[u].r - tr[u].l + 1) * d;
        tr[u].add += d;
    }
    else
    {
        pushdown(u);
        int mid = tr[u].l + tr[u].r >> 1;

        if (l <= mid) modify(u << 1, l, r, d);

        if (r > mid) modify(u << 1 | 1, l, r, d);

        pushup(u);
    }
}

int query(int u, int l , int r)
{
    if (tr[u].l >= l && tr[u].r <= r) return tr[u].sum;

    pushdown(u);
    int mid = tr[u].l + tr[u].r >> 1;
    int sum = 0;

    if (l <= mid)
        sum = query(u << 1, l, r);

    if (r > mid) sum += query(u << 1 | 1, l, r);

    return sum;
}

signed main()
{
    IOS;
    cin >> n >> m;

    for (int i = 1; i <= n; i++)
        cin >> w[i];

    build(1, 1, n);
    char op[2];
    int l, r, d;

    while (m--)
    {
        cin >> op >> l >> r;

        if (op[0] == 'C')
        {
            cin >> d;
            modify(1, l, r, d);
        }
        else
            cout << query(1, l, r) << endl;
    }

    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

追寻远方的人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值