跟着董晓老师写的线段树板子题:
image
代码如下:

#define ll long long
#define lc p<<1         // 左孩子节点编号:p * 2
#define rc p<<1|1       // 右孩子节点编号:p * 2 + 1
using namespace std;

const int N = 1e5 + 5;  // 数据范围,根据题目调整

// 线段树节点结构
struct Node {
    int l, r;          // 当前节点代表的区间 [l, r]
    ll sum;            // 当前区间的和
    ll add;            // 懒标记,用于延迟更新区间操作
} tree[N << 2];         // 线段树数组,大小开4倍防止越界

ll w[N];               // 原始数组,存储初始数据

// 向上更新:用左右子节点的和更新当前节点的和
void pushup(int p) {
    // 当前节点的和 = 左子树的和 + 右子树的和
    tree[p].sum = tree[lc].sum + tree[rc].sum;
}

// 向下传递懒标记:将当前节点的懒标记传递给左右子节点
void pushdown(int p) {
    if (tree[p].add) {  // 如果当前节点有未传递的懒标记
        // 更新左子节点的和:区间长度 * 懒标记值
        tree[lc].sum += tree[p].add * (tree[lc].r - tree[lc].l + 1);
        // 更新右子节点的和:区间长度 * 懒标记值
        tree[rc].sum += tree[p].add * (tree[rc].r - tree[rc].l + 1);
        
        // 传递懒标记到左子节点
        tree[lc].add += tree[p].add;
        // 传递懒标记到右子节点
        tree[rc].add += tree[p].add;
        
        // 清除当前节点的懒标记
        tree[p].add = 0;
    }
}

// 构建线段树
// p:当前节点编号,l:当前区间左端点,r:当前区间右端点
void build(ll p, ll l, ll r) {
    // 初始化当前节点的区间、和、懒标记
    tree[p] = {l, r, w[l], 0};
    if (l == r) return;  // 叶子节点,直接返回
    
    int mid = (l + r) >> 1;  // 计算区间中点
    build(lc, l, mid);       // 递归构建左子树
    build(rc, mid + 1, r);   // 递归构建右子树
    pushup(p);               // 向上更新当前节点的和
}

// 区间更新:将区间 [x, y] 的每个数加上 k
// p:当前节点编号,x,y:目标区间,k:增量
void update(int p, int x, int y, ll k) {
    // 如果当前节点区间完全包含在 [x, y] 中
    if (x <= tree[p].l && tree[p].r <= y) {
        // 更新当前节点的和:区间长度 * k
        tree[p].sum += (tree[p].r - tree[p].l + 1) * k;
        // 记录懒标记,延迟更新子节点
        tree[p].add += k;
        return;
    }
    
    pushdown(p);  // 否则,先传递懒标记到子节点
    
    int m = (tree[p].l + tree[p].r) >> 1;  // 计算区间中点
    // 如果目标区间与左子树区间有交集,递归更新左子树
    if (x <= m) update(lc, x, y, k);
    // 如果目标区间与右子树区间有交集,递归更新右子树
    if (y > m) update(rc, x, y, k);
    
    pushup(p);  // 向上更新当前节点的和
}

// 区间查询:查询区间 [ql, qr] 的和
// p:当前节点编号,ql,qr:查询区间
ll query(ll p, ll ql, ll qr) {
    // 如果当前节点区间完全包含在 [ql, qr] 中,直接返回当前节点的和
    if (ql <= tree[p].l && tree[p].r <= qr) {
        return tree[p].sum;
    }
    
    pushdown(p);  // 否则,先传递懒标记到子节点
    
    ll m = (tree[p].l + tree[p].r) >> 1;  // 计算区间中点
    ll sum = 0;
    // 如果查询区间与左子树区间有交集,递归查询左子树
    if (ql <= m) sum += query(lc, ql, qr);
    // 如果查询区间与右子树区间有交集,递归查询右子树
    if (qr > m) sum += query(rc, ql, qr);
    
    return sum;  // 返回查询区间的和
}

int main() {
    ios::sync_with_stdio(false);  // 加速输入输出
    cin.tie(0);
    
    int n, m;
    cin >> n >> m;  // 读入数据规模 n 和操作数 m
    
    // 读入原始数组
    for (int i = 1; i <= n; i++) {
        cin >> w[i];
    }
    
    // 构建线段树,根节点编号为 1,区间为 [1, n]
    build(1, 1, n);
    
    // 处理 m 个操作
    for (int i = 1; i <= m; i++) {
        int op;
        cin >> op;  // 读入操作类型
        
        if (op == 1) {  // 区间更新操作:将 [x, y] 的每个数加上 k
            ll x, y, k;
            cin >> x >> y >> k;
            update(1, x, y, k);
        } else {  // 区间查询操作:查询 [x, y] 的和
            ll x, y;
            cin >> x >> y;
            cout << query(1, x, y) << endl;
        }
    }
    
    return 0;
}``