There is an integer array, whose elements have an increment limit after which they turn to zero (for example ..., 34, 35, 0, 1, 2 ...), and you need to read the sum of elements between certain positions, output it, after which these elements increment, then do the same thing again, but perhaps between different positions. Accordingly, the number of operations will be equal to the sum of all intervals of summation multiplied by two + checks of elements on the border crossing, and this is quite a long time. Is it possible to somehow optimize this process taking into account the cyclical nature of the processes?
- For an array, the module is specified in advance (the same value after which the element becomes 0?). Is this module set one for the whole array? In the process of counting the module changes - gbg
- @gbg Yes, the module is known in advance. No, it does not change. - Ka_ktus 2:58 pm
- new range for computing crosses the old one? The distribution law of the initial data on the array is? - gbg
- @gbg Not necessarily, but it can cross, that was the question of optimization. Initially the array is filled with random positive integers. The counting areas are previously correct, the numbers of the first elements in the area and the numbers of the latter are known, they do not go beyond the array - Ka_ktus
- so far, apart from the obvious parallelization and the fact that if a module is a power of two, then it is easy to count, I do not see accelerations. New interval becomes known after the first interval is calculated? Or are all the summation intervals known in advance? - gbg
1 answer
The task is clearly olympiadna but oh well.
The general idea is a segment tree with lazy operations and maintaining a balance of vertices. For simplicity, we fix the value in MOD. This will not complicate the task much, but it will be easier to debug.
Yes, there are a lot of multiplications by 2, you can replace x+x x<<1 and so on as you like.
The main link is http://e-maxx.ru/algo/segment_tree
Element structure
struct Node{ long sum; int CountV[MOD]; int pending; } We will need support functions:
void AddMod(int CountV[MOD], int delt){ //выполняет сдвиг массива int temp[MOD]; //(можно оптимальнее) memcpy(temp,CountV,MOD*sizeof(int)); for (int i=0;i<MOD;i++) CountV[(i + delt) % MOD] = temp[i]; } void addV(int &x, int y){ //добавляет число по модулю x+=y; x%=MOD; } void relax(int U, int d){ //добавляет в вершине отложенно d AddMod(U, d); addV(Tree[U].pending, d); Tree[U].sum = 0; for (int i = 0;i<10;i++) Tree[U].sum += i*Tree[U].CountV[i]; } void forcePush(int U){ //"пропихивает" через вершину отложенное relax(2*U,Tree[U].pending); relax(2*U + 1,Tree[U].pending); Tree[U].pending = 0; Tree[U].sum = 0; for (int i = 0;i<10;i++) Tree[U].sum += i*(Tree[U].CountV[i] = Tree[2 * U].CountV[i] + Tree[2 * U + 1].CountV[i]); } Further we do a standard tree a root in 1 !:
void add(int U, int L, int R, int l, int r, int val) { //l,r - нужный if (L >= r || R <= l) return; //отрезок l = max(l, L); r = min(R, r); if (L == l && R == r) { relax(U,val); return; } forcePush(U); add(2 * U, L, (L + R) >> 1, l, r, val); add(2 * U + 1, (L + R) >> 1, R, l, r, val); forcePush(U); } long getSum(int U, int L, int R, int l, int r) { if (L >= r || R <= l) return 0; l = max(l, L); r = min(R, r); if (L == l && R == r) return Tree[U].sum; forcePush(U); long res = getSum(2 * U, L, (L + R) >> 1, l, r) + getSum(2 * U + 1, (L + R) >> 1, R, l, r); return res; } void init(int U, int L, int R){ if (L >= R) return; if (L + 1 == R){ Tree[U].CountV[ INIT[L] ] = 1; Tree[U].sum = INIT[L]; return; } init(2*U,L, (L + R) >> 1); init(2*U+1,(L + R) >> 1,R); for (int i = 0;i<10;i++) Tree[U].sum += i*(Tree[U].CountV[i] = Tree[2 * U].CountV[i] + Tree[2 * U + 1].CountV[i]); } Once again, the idea is that at the top of the tree there is a value how much was pushed there before. When it is necessary to count the sum, we simply stretch these values, when we go down, we push this value. Then everything seems to be simple, I recommend to read this tree completely.
Calls - getSum(1,0,MaxN, left, rigth);
At the request of more.
At each vertex we know the number of elements equal to i in its segment.
Imagine that the add request will be exactly the segment covered by the vertex. Then we put a mark at the top of what's added there and that's it. If the coverage is not complete, then there is no choice, go left and right, we do until it completely coincides. If there was something at the top and we were going to go down, then we immediately push the value down (you can shove with the sum, there is no difference, just debugging is easier).
Now the amount. If we come to a vertex that contains exactly the required segment, then we can easily calculate the sum in it from the array. (the sum of i*Count[i] ). If not, go down and push through the value. Now that we have reached such a vertex, we need to perform a deferred summation. To do this, simply call the AddMod function described above. I think it is not necessary to explain what she does.
Full build code http://ideone.com/1vuNAm .
Here you can optimize (for example, 2 calls forcePush(U); add not needed). But I will leave it as an exercise :)
- Thank! Could you explain, if not difficult, is it possible to implement a "cyclic" increment on the segment tree, the implementation of which is presented at the link provided to you? (That implementation, to be honest, is just much clearer than yours. Or should I better understand your code?) - Ka_ktus
- @Ka_ktus is the base implementation. I made a cyclical sum over it. So first the tree is by reference, then my code. If I find it, I once wrote my code for a similar task. - pavel
- Can you verbally describe (at least approximately) how does the cyclical nature of the sums work in this code? - Ka_ktus
- @Ka_ktus added. But better, you first write a regular tree, then I think it will become clearer. - pavel
- Good. One more question: if I understood everything correctly, the
add(...)function builds a segment tree. If so, based on what does she do it? The initial array is necessary, it seems. If not, then why is it, because it is not used anywhere else? And can you explain what the pending variable is? With the rest everything seems to be clear - Ka_ktus