В последнем последнем руководстве мы узнали, как построить дерево сегментов, запросить его и обновить в нем значения точек. В этом руководстве мы изучим полезную технику Ленивое распространение для деревьев сегментов. Используя ленивое распространение, мы можем эффективно обновлять диапазон в дереве. В этом методе мы обновляем значение ленивым образом, поскольку название напрашивается само. Из-за ленивости значения не обновляются без необходимости. Следовательно, «ленивое распространение» примерно означает «ленивое» распространение значений из корневого каталога.

Поясним на примере. Предположим, мы построили дерево сегментов, и в нем доступны два типа запросов. Первый - это обновление, которое добавляет некоторое значение узлам в определенном диапазоне. Второй тип - это запрос суммы, который запрашивает сумму значений узлов для заданного диапазона. В ленивом распространении термин «ленивый» относится к идее, что мы не будем обновлять диапазон вплоть до конечных узлов при каждом его вызове. Поскольку запрос суммы вызывается последним, мы будем накапливать значения в другом массиве каждый раз, когда вызывается update (), и эти накопленные значения будут предоставлять сумму только для запроса, который запрашивает сумму в диапазоне. Давайте рассмотрим пример, чтобы прояснить концепцию.

Вопрос
Вам дается массив из N элементов, которые изначально равны нулю. После этого вам будут даны команды C. Это:
* 0 p q v - вам нужно добавить v ко всем числам в диапазоне от p до q (включительно), где p и q - два индекса массива.

* 1 p q - вывести строку, содержащую одно целое число, которое является суммой всех элементов массива между p и q (включительно)

Ввод:
В первой строке вы увидите T, количество тестов.

Каждый тестовый пример начинается с N (N ‹= 100 000) и C (C‹ = 100 000). После этого вам будут предоставлены команды C в указанном выше формате. 1 ‹= p, q‹ = N и 1 ‹= v‹ = 10⁷.

Вывод:
Распечатайте ответы на вопросы.

Источник вопроса: SPOJ - УЖАСНО

import java.io.BufferedReader;
import java.io.IOException;
import java.util.Arrays;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
import java.io.FileInputStream;
class lazypropagation {
 long tree[];  //the Segment tree 
 long helper[]; //this array accumulates value to be updated
 //if true, values are pending for this node to be updated
 boolean flag[];
 
 lazypropagation(){
  tree = new long [262145];
        helper = new long [262145];
        flag  = new boolean [262145];
        
        //initialize all node of tree and helper with zero
  Arrays.fill (tree, 0);
        Arrays.fill (helper, 0);
        
        //initially set all flags to false as their is no pending value for any node
        Arrays.fill (flag, false);
 }
 
 
 /**
  * @param nodeToUpdate node in tree to be updated
  * @param valueToUpdate value to be updated
  * @param left is beginning index of array
  * @param right is ending index of array
  * @param start is beginning index of query
  * @param end is ending index of query
  **/
 private void update (int node, int value, int left, int right, int start, int end){
  /**
   *   Legends:
   *   S = Start, E = End, L = Left, R = Right
   *   dotted lines ---- represents array
   *   square brackets [ ] denote range of query
   * 
   **/
  
  // When range to be updated does not intersect 
  // with the array indices [left, right]
  // ...E]  L------R [S...
   if ( end <left || start > right )
            return ;
         
        // left == right means we've reached to a single element of array. 
        if (left == right ){
            tree[node]+= value;
            
            //flag is true mean some value is pending in helper[node]
            //so we should add this to tree[node] and clear helper[node]
            if (flag[node]==true){
                tree[node] += helper[node];
                helper[node]=0;
            }
            return;
        }
        
        int leftChild = 2*node;
        int rightChild = 2*node + 1;
        
        //Each of the below case finds a range by right-left+1 or end-left+1
        //this range should be multiplied by value and should be added to tree[node]
        
        //   [S   L------R E]
        if (start <= left && right <= end){
            helper[leftChild] += value;
            helper[rightChild] += value;
            
            flag[leftChild] = flag[rightChild] = true;
            
            tree[node] += (value *(right - left +1));
            return ;
        }
        //  ---[S---R   E]
        else if (start <= right &&  right < end)
            tree[node ] += (value*(right - start +1));
        
        //  [S    L----E]----
        else if (start < left && left <= end )
            tree[node] += (value*(end - left + 1));
            
        //  ----L----[S-----E]-----R-----
        else if (left <= start && end <= right)
            tree[node] += (value*(end - start + 1));
 
        update (leftChild, value, left, (left+right)/2, start, end );
        update (rightChild, value, ((left+right)/2)+1, right, start, end);
 }
 
 private long sum_of_range (int node, int left, int right, int start, int end){
  // query does not overlaps with array range
  // ---R---- [S  E]----L-----
        if (right < start || end < left)
            return 0;
            
        int leftChild = 2*node;
  int rightChild = 2*node+1;
  
  // true means value updates are pending for the node
        if (flag[node] == true){
   
   //update value in tree node
   // right-left+1 gives the range for which 
   //values to be updated in parent node which here is tree[node]
            tree[node] += (helper[node] *(right-left+1));
            
            //after values are updated, set flag to false
            flag[node]=false;
            
            // if it's not a leaf node
            if (left != right){
    
    //push the flag downward to children of node because
    //after recursive call these children will act as parent
    //and then tree[node] would be calculated
                flag [ leftChild ] = flag [ rightChild ] = true;
                 
                //push down the parent value to leftChild and rightChild
                //hence updated values for child nodes would be the sum of their own values + parent node value
                helper[ leftChild ] += helper[node];
                helper[ rightChild ] += helper[node];
            }
            
            //as value of helper[node] is already added to its children
            // set helper[node] = 0 to receive fresh values
            helper[node]=0;
        }
        
  // return value of tree[node] as query completely overlaps the array range
  // hence no need to go further on its children nodes.
  // [S---L---R---E]
        if (start <= left && right <= end){
            return tree[node];
        }
        
        //sum returned for left half of tree intersecting with query
        long s1 = sum_of_range(leftChild, left, (left+right)/2, start, end);
        
        //sum returned for right half of tree intersecting with query
        long s2 = sum_of_range(rightChild, ((left+right)/2)+1, right, start, end);
        return s1+s2;
    }
    
 public static void main (String [] args) throws IOException{
  // for reading input from console use new InputStreamReader (System.in)
        BufferedReader br = new BufferedReader (new InputStreamReader (new FileInputStream("input.txt")));
        StringTokenizer st =null;
        int t = Integer.parseInt(br.readLine());
        for(;t>0;t--){
            st = new StringTokenizer (br.readLine());
            int N = Integer.parseInt(st.nextToken());
            int C = Integer.parseInt(st.nextToken());
            lazypropagation segtree = new lazypropagation();
            for (int I=1;I<=C;I++){
                st = new StringTokenizer(br.readLine());
                if (st.nextToken().equals("0")){
                    int P = Integer.parseInt(st.nextToken());
                    int Q = Integer.parseInt(st.nextToken());
                    int V = Integer.parseInt(st.nextToken());
                    segtree.update (1, V, 1, N, P,Q);
                }
                else {
                    int P = Integer.parseInt(st.nextToken());
                    int Q = Integer.parseInt(st.nextToken());
                    long ans = segtree.sum_of_range (1,1,N, P,Q);
                    System.out.println(ans);
                }
            }
        }
    }
}

В приведенном выше коде update () отвечает как за построение дерева, так и за обновление значений в нем. В update () мы взяли индексы массива слева от 1 и справа до N, поскольку N - это количество элементов в массиве. Существует четыре возможных случая пересечения диапазона массива и диапазона запроса. Все эти случаи представлены в комментариях update (). Основная идея update () состоит в том, чтобы накапливать обновляемое значение для тех узлов, которые являются пересечением диапазона запроса и диапазона массива в helper [node]. Эти значения будут накапливаться для каждого update () и будут обрабатываться при вызове sum_of_range (). Я добавил много комментариев в код, чтобы он не требовал пояснений. Тем не менее, если возникнут сомнения, оставьте комментарий. Спасибо за чтение. Хорошего дня!!!