社区讨论

悬棺求解(写法问题)

P13825【模板】线段树 1.5参与者 1已保存回复 0

讨论操作

快速查看讨论及其快照的属性,并进行相关操作。

当前回复
0 条
当前快照
1 份
快照标识符
@mmhp303k
此快照首次捕获于
2026/03/08 19:54
前天
此快照最后确认于
2026/03/09 12:44
22 小时前
查看原帖

考虑这两种写法,一种将区间放在存的信息里,如下

CPP

struct node {
    int l, r;       // 区间的左右边界
    ll sum;         // 区间和
    int add;        // 区间加值
    int ls, rs;     // 左右孩子节点
} tr[N << 5];

int cnt = 0;

// Pushdown操作:下传懒标记
void pushdown(int c) {
    auto &t = tr[c];
    if (t.add == 0 || t.l == t.r) return;
    int mid = (t.l + t.r) / 2;  // 计算中点
    if (t.add != 0) {  // 如果有懒标记
        if (t.ls == 0) t.ls = ++cnt;
        if (t.rs == 0) t.rs = ++cnt;

        // 下传加值到左右孩子
        tr[t.ls].sum = tr[t.ls].sum + (mid - t.l + 1) * t.add;
        tr[t.rs].sum = tr[t.rs].sum + (t.r - mid) * t.add;
        tr[t.ls].add = tr[t.ls].add + t.add;
        tr[t.rs].add = tr[t.rs].add + t.add;

        t.add = 0;  // 清空懒标记
    }
}

// 更新操作:更新区间 [L, R] 上的值
void update(int &c, int L, int R, int k, int l, int r) {
    if (!c) c = ++cnt;  // 如果当前节点为空,分配一个新节点
    auto &t = tr[c];
    t.l = l;
    t.r = r;  // 设置区间边界

    if (L <= t.l && R >= t.r) {
        t.sum = t.sum + (t.r - t.l + 1) * k;
        t.add = t.add + k;
        return;
    }
    pushdown(c);  // 推动懒标记
    int mid = (t.l + t.r) / 2;
    if (L <= mid) update(t.ls, L, R, k, t.l, mid);  // 更新左子树
    if (R > mid) update(t.rs, L, R, k, mid + 1, t.r);  // 更新右子树
    t.sum = 0;
    if (t.ls) t.sum = t.sum + tr[t.ls].sum;  // 汇总左子树的和
    if (t.rs) t.sum = t.sum + tr[t.rs].sum;  // 汇总右子树的和
}

// 查询操作:查询区间 [L, R] 上的和
ll query(int c, int L, int R) {
    if (!c) return 0;
    auto &t = tr[c];
    if (L <= t.l && t.r <= R) return t.sum;
    pushdown(c);  // 推动懒标记
    int mid = (t.l + t.r) / 2;
    ll res = 0;
    if (L <= mid) res = res + query(t.ls, L, R);  // 查询左子树
    if (R > mid) res = res + query(t.rs, L, R);  // 查询右子树
    return res;
}

另一种写法是在每次函数里传参

CPP
void pushdown(int c,int l,int r) {
    auto &t = tr[c];
    if (t.add == 0 || l == r) return;
    int mid = (l + r) / 2;  // 计算中点
    if (t.add != 0) {  // 如果有懒标记
        if (t.ls == 0) t.ls = ++cnt;
        if (t.rs == 0) t.rs = ++cnt;

        // 下传加值到左右孩子
        tr[t.ls].sum = tr[t.ls].sum + (mid - l + 1) * t.add;
        tr[t.rs].sum = tr[t.rs].sum + (r - mid) * t.add;
        tr[t.ls].add = tr[t.ls].add + t.add;
        tr[t.rs].add = tr[t.rs].add + t.add;

        t.add = 0;  // 清空懒标记
    }
}

// 更新操作:更新区间 [L, R] 上的值
void update(int &c, int L, int R, int k, int l, int r) {
    if (!c) c = ++cnt;  // 如果当前节点为空,分配一个新节点
    auto &t = tr[c];


    if (L <= l && R >= r) {
        t.sum = t.sum + (r - l + 1) * k;
        t.add = t.add + k;
        return;
    }
    pushdown(c,l,r);  // 推动懒标记
    int mid = (l + r) / 2;
    if (L <= mid) update(t.ls, L, R, k, l, mid);  // 更新左子树
    if (R > mid) update(t.rs, L, R, k, mid + 1, r);  // 更新右子树
    t.sum = 0;
    if (t.ls) t.sum = t.sum + tr[t.ls].sum;  // 汇总左子树的和
    if (t.rs) t.sum = t.sum + tr[t.rs].sum;  // 汇总右子树的和
}

// 查询操作:查询区间 [L, R] 上的和
ll query(int c, int L, int R,int l,int r) {
    if (!c) return 0;
    auto &t = tr[c];
    if (L <= l && r <= R) return t.sum;
    pushdown(c,l,r);  // 推动懒标记
    int mid = (l + r) / 2;
    ll res = 0;
    if (L <= mid) res = res + query(t.ls, L, R,l,mid);  // 查询左子树
    if (R > mid) res = res + query(t.rs, L, R,mid+1,r);  // 查询右子树
    return res;
}

求问第一种写法为什么错误,(如果可以的话)如何修改

回复

0 条回复,欢迎继续交流。

正在加载回复...