谈谈今天写线段树时碰到的问题

时间:2022-07-08 19:57:56

今天打一个线段树,求区间最值的。

它有一个这样的更新函数定义:

 1 int insert(int rt, int l, int r) {
 2     if (r <= O || l >= D) return 0;
 3     if (O <= l && r <= D) {
 4         tree[rt].val += N;
 5         tree[rt].mark += N;
 6         return tree[rt].val;
 7     }
 8     int mid = (l + r) >> 1;
 9     pushdown(rt);
10     return tree[rt].val = std::max(insert(lson, l, mid), insert(rson, mid, r));
11 }

注意10行return的写法。

以及下面一个同样功能的函数定义:

 1 void insert(int rt, int l, int r) {
 2     if (r <= O || l >= D) return;
 3     if (O <= l && r <= D) {
 4         tree[rt].val += N;
 5         tree[rt].mark += N;
 6         return;
 7     }
 8     int mid = (l + r) >> 1;
 9     pushdown(rt);
10     insert(lson, l, mid);
11     insert(rson, mid, r);
12     tree[rt].val = std::max(tree[lson].val, tree[rson].val);
13 }

它们达到的目的表面上看是相同的。但实际评测时,正是因为这两个函数的写法不同,导致了答案不一样。

后者的结果时正确的。

而如果这么写,也是错的:
错误定义1:

 1 int insert(int rt, int l, int r) {
 2     if (r <= O || l >= D) return 0;
 3     if (O <= l && r <= D) {
 4         tree[rt].val += N;
 5         tree[rt].mark += N;
 6         return tree[rt].val;
 7     }
 8     int mid = (l + r) >> 1;
 9     pushdown(rt);
10     int tmp1 = insert(lson, l, mid), tmp2 = insert(rson, mid, r);
11     return tree[rt].val = std::max(tmp1, tmp2);
12 }

错误定义2:

 1 int insert(int rt, int l, int r) {
 2     if (r <= O || l >= D) return 0;
 3     if (O <= l && r <= D) {
 4         tree[rt].val += N;
 5         tree[rt].mark += N;
 6         return tree[rt].val;
 7     }
 8     int mid = (l + r) >> 1;
 9     pushdown(rt);
10     int tmp1 = insert(lson, l, mid), tmp2 = insert(rson, mid, r);
11     tree[rt].val = std::max(tmp1, tmp2);
12     return tree[rt].val;
13 }

后来,我思考了很久,终于发现了错误的原因所在:

注意它们都有一条相同的语句:

if (r <= O || l >= D) return 0;

正是因为这一条语句,当该节点所代表的区间不完全被更新区间包含时,会访问其左右子节点,在处理完其左右节点时,若有一个节点代表的区间与更新区间的交集为空,那么该节点的上传的val值就会被0所覆盖,而实际上这个节点的val值可能是非零的,甚至影响更新,导致错误。

总结

比赛时,这种问题非常难考虑到, 因为表面上看这样写确实是正确的。因此仔细考虑程序的每一个步骤。