今天打一个线段树,求区间最值的。
它有一个这样的更新函数定义:
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值可能是非零的,甚至影响更新,导致错误。
总结
比赛时,这种问题非常难考虑到, 因为表面上看这样写确实是正确的。因此仔细考虑程序的每一个步骤。