好吧,我试图在Codechef上解决这个Flipping coins问题。用分段树解决它。但是时间限制超过了。我搜索并发现我必须使用懒惰传播。但我无法理解。我的更新功能递归地工作(从上到下)。请提供一些提示或用例子解释。还要指出我必须更改代码的位置。
在翻转硬币时,如果节点值为1,则在更新期间将其更改为0,如果为0,则将其更改为1.
开始和结束是原始数组的限制。树是分段树阵列。
void update(int node, int start, int end,int pos)//pos=position to update
{
if(start==end) tree[node]=(tree[node]==0) ? 1 : 0;
else
{
int mid=(start+end)/2;
if(mid>=pos) update(2*node + 1, start, mid, pos);
else update(2*node + 2, mid+1, end, pos);
tree[node]=tree[2*node +1] + tree[2*node +2];
}
}
答案 0 :(得分:9)
延迟传播意味着仅在需要时进行更新。它是一种允许以渐近时间复杂度O(logN)执行范围更新的技术(这里N是范围)。
假设您要更新范围[0,15],然后更新节点[0,15]并在节点中设置一个标志,表示要更新子节点(使用sentinel值如果没有使用该标志)。
可能的压力测试案例:
0 1 100000
0 1 100000
0 1 100000 ...重复Q次(其中Q = 99999) 第100000个查询将是
1 1 100000
在这种情况下,大多数实施人员只会翻转100000个硬币99999次,以便在最后和超时时回答一个简单的查询。
使用延迟传播,您只需要翻转节点[0,100000] 99999次并设置/取消设置其子项将要更新的标志。当询问实际查询本身时,您开始遍历其子项并开始翻转它们,将标志向下推并取消设置父标志。
哦,确保你正在使用正确的I / O例程(scanf和printf而不是cin和cout,如果它的c ++) 希望这能让您了解延迟传播的含义。 更多信息 : http://www.spoj.pl/forum/viewtopic.php?f=27&t=8296
答案 1 :(得分:2)
我将延迟更新操作与正常更新操作以及如何更改查询操作进行对比。
在正常的单个更新操作中,您更新树的根,然后递归地仅更新树的所需部分(从而为您提供O(log(n))
速度)。如果您尝试使用相同的逻辑进行范围更新,您可以看到它如何恶化到O(n)
(考虑非常宽的范围,并且看到您将主要需要更新树的两个部分)。
因此,为了克服这个O(n)
的想法,只有在你真正需要时才更新树(在以前更新的段上查询/更新,从而使你的更新变得懒惰)。所以这是它的工作原理:
以下是更新和查询(解决最大范围查询)的示例。对于full code - check this article。
void update_tree(int node, int a, int b, int i, int j, int value) {
if(lazy[node] != 0) { // This node needs to be updated
tree[node] += lazy[node]; // Update it
if(a != b) {
lazy[node*2] += lazy[node]; // Mark child as lazy
lazy[node*2+1] += lazy[node]; // Mark child as lazy
}
lazy[node] = 0; // Reset it
}
if(a > b || a > j || b < i) // Current segment is not within range [i, j]
return;
if(a >= i && b <= j) { // Segment is fully within range
tree[node] += value;
if(a != b) { // Not leaf node
lazy[node*2] += value;
lazy[node*2+1] += value;
}
return;
}
update_tree(node*2, a, (a+b)/2, i, j, value); // Updating left child
update_tree(1+node*2, 1+(a+b)/2, b, i, j, value); // Updating right child
tree[node] = max(tree[node*2], tree[node*2+1]); // Updating root with max value
}
和查询:
int query_tree(int node, int a, int b, int i, int j) {
if(a > b || a > j || b < i) return -inf; // Out of range
if(lazy[node] != 0) { // This node needs to be updated
tree[node] += lazy[node]; // Update it
if(a != b) {
lazy[node*2] += lazy[node]; // Mark child as lazy
lazy[node*2+1] += lazy[node]; // Mark child as lazy
}
lazy[node] = 0; // Reset it
}
if(a >= i && b <= j) // Current segment is totally within range [i, j]
return tree[node];
return max(query_tree(node*2, a, (a+b)/2, i, j), query_tree(1+node*2, 1+(a+b)/2, b, i, j));
}
答案 2 :(得分:1)
要将5..15标记为“是”,以下是您所需要的全部内容。操作中只涉及7个节点,远小于不使用延迟传播的情况。可以证明,最多可能涉及2logn - 1
个节点,其中n
是范围。
0..31u
/ \
0..15u 16..31n
/ \
0..8u 9..15y
/ \
0..4n 5..8y
(u: unknown, look deeper; y: yes; n: no)
没有延迟传播,段树并不比普通数组好。
有关如何实施的更多详细信息由您自行决定。你应该自己解决。