I am implementing a segment tree, to be able to answer quickly to the following queries in an array A:
- query i, j: sum of all elements in range (i,j)
- update i, j, k: add k to all elements in the range(i,j)
Here is my implementation:
typedef long long intt;
const int max_num=100000,max_tree=4*max_num;
intt A[max_num],ST[max_tree];
void initialize(int node, int be, int en) {
if(be==en) {
ST[node]=ST[be];
} else {
initialize(2*node+1,be,(be+en)/2);
initialize(2*node+2,(be+en)/2+1,en);
ST[node]=ST[2*node+1]+ST[2*node+2];
}
}
void upg(int node, int be, int en, int i, intt k) {
if(be>i || en<i || be>en) return;
if(be==en) {
ST[node]+=k;
return;
}
upg(2*node+1, be, (be+en)/2, i, k);
upg(2*node+2, (be+en)/2+1, en, i, k);
ST[node] = ST[2*node+1]+ST[2*node+2];
}
intt query(int node, int be, int en, int i, int j) {
if(be>j || en<i) return -1;
if(be>=i && en<=j) return ST[node];
intt q1=query(2*node+1, be, (be+en)/2, i, j);
intt q2=query(2*node+2, (be+en)/2+1, en, i, j);
if(q1==-1) return q2;
else if(q2==-1) return q1;
else return q1+q2;
}
The query function is really fast, its complexity is O(lg N), where N is j-i. The update function is also fast in the average case, but when j-i is big, the complexity of the update is O(N lg N), which is not fast at all.
I have searched the subject a bit, and I found that if I implement the segment tree with lazy propagation, the complexity of both query and update is O(lg N), which is asymptotically faster than O(N lg N).
I also found a link to an other question, which has a really nice implementation of a segment tree, that uses pointers: How to implement segment trees with lazy propagation?. So, here is my question: Is there a simpler way to implement lazy propagation, without using pointers, but with array indexes, and without a segment_tree data structure?
This is me playing around with this data structure and some template tomfoolery.
At the bottom of all of this mess is accessing two flat arrays, one of them containing a tree of sums, the other containing a tree of carry values to propogate down later. Conceptually they form one binary tree.
The true value of a node in the binary tree is the value in stored sum tree, plus the number of leaves under the node times the sum of all carry tree values from the node back up to the root.
At the same time, the true value of each node in the tree is equal to the true value of each leaf node under it.
I wrote one function to do both carry and sum, because it turns out they are visiting the same nodes. And a read sometimes writes. So you get a sum by calling it with an
increaseof zero.All the template tomfoolery does is do the math for where the offsets into each tree the nodes are, and where the left and right children are.
While I do use a
struct, thestructis transient — it is just a wrapper with some pre calculated values around an offset into an array. I do store a pointer to the start of the array, but everyblock_ptruses the exact samerootvalue in this program.For debugging, I have some craptacular Assert() and Debug() macros, plus a trace nullary function that the recursive sum function calls (which I use to track the total number of calls into it). Once again, being needlessly complex to avoid global state. 🙂
Here are some helper classes to make creating a segment tree of a given size easy. Note, however, that all you need is an array of the right size, and you can construct a block_ptr from a pointer to element 0, and you are good to go.
While this isn’t the answer you want, it might make it easier for someone to write it. And writing this amused me. So, hope it helps!
The code was tested and compiled on Ideone.com using a C++0x compiler.