Skip to content

Sqrt Decomposition🔗

  • Divides the array into blocks of size roughly .
  • Each block stores the minimum value of that block.
  • Lazy array stores pending updates for each block.

Range Updates

  • If the update range is within a single block, apply updates directly to elements and rebuild that block
  • If the update range spans multiple blocks:
    • Update partial blocks at the edges directly.
    • For full blocks in between, store lazy updates and update block minimum directly.

Range Minimum Query

  • If query range is within a single block, check elements directly.
  • Otherwise, check partial blocks at edges and use stored block minimum or lazy values for full blocks in between.
  • Using ⁠`INT_MAX as sentinel for "no update" can cause problems if ⁠INT_MAX is a valid value.
#include <bits/stdc++.h>
using namespace std;

class SqrtDecomposition {
private:
    int n, blockSize, numBlocks;
    vector<int> A;          // original array
    vector<int> blocks;     // stores minimum of each block
    vector<int> lazy;       // lazy updates for each block

    void applyLazy(int block) {
        if (lazy[block] != INT_MAX) {
            int start = block * blockSize;
            int end = min(n, start + blockSize);
            for (int i = start; i < end; i++) {
                A[i] = lazy[block];
            }
            blocks[block] = lazy[block];
            lazy[block] = INT_MAX;
        }
    }

    void rebuildBlock(int block) {
        int start = block * blockSize;
        int end = min(n, start + blockSize);
        int mn = INT_MAX;
        for (int i = start; i < end; i++) {
            mn = min(mn, A[i]);
        }
        blocks[block] = mn;
    }

public:
    SqrtDecomposition(const vector<int> &initialA) {
        A = initialA;
        n = (int)A.size();
        blockSize = (int)sqrt(n);
        numBlocks = (n + blockSize - 1) / blockSize;
        blocks.assign(numBlocks, INT_MAX);
        lazy.assign(numBlocks, INT_MAX);

        for (int i = 0; i < numBlocks; i++) {
            rebuildBlock(i);
        }
    }

    // Range update: set all elements in [l, r] to val
    void rangeUpdate(int l, int r, int val) {
        int startBlock = l / blockSize;
        int endBlock = r / blockSize;

        if (startBlock == endBlock) {
            applyLazy(startBlock);
            for (int i = l; i <= r; i++) {
                A[i] = val;
            }
            rebuildBlock(startBlock);
        } else {
            // Partial first block
            applyLazy(startBlock);
            int startBlockEnd = (startBlock + 1) * blockSize - 1;
            for (int i = l; i <= startBlockEnd; i++) {
                A[i] = val;
            }
            rebuildBlock(startBlock);

            // Full blocks in between
            for (int b = startBlock + 1; b < endBlock; b++) {
                lazy[b] = val;
                blocks[b] = val;
            }

            // Partial last block
            applyLazy(endBlock);
            int endBlockStart = endBlock * blockSize;
            for (int i = endBlockStart; i <= r; i++) {
                A[i] = val;
            }
            rebuildBlock(endBlock);
        }
    }

    // Range minimum query [l, r]
    int rangeMinQuery(int l, int r) {
        int startBlock = l / blockSize;
        int endBlock = r / blockSize;
        int mn = INT_MAX;

        if (startBlock == endBlock) {
            applyLazy(startBlock);
            for (int i = l; i <= r; i++) {
                mn = min(mn, A[i]);
            }
        } else {
            // Partial first block
            applyLazy(startBlock);
            int startBlockEnd = (startBlock + 1) * blockSize - 1;
            for (int i = l; i <= startBlockEnd; i++) {
                mn = min(mn, A[i]);
            }

            // Full blocks in between
            for (int b = startBlock + 1; b < endBlock; b++) {
                if (lazy[b] != INT_MAX) {
                    mn = min(mn, lazy[b]);
                } else {
                    mn = min(mn, blocks[b]);
                }
            }

            // Partial last block
            applyLazy(endBlock);
            int endBlockStart = endBlock * blockSize;
            for (int i = endBlockStart; i <= r; i++) {
                mn = min(mn, A[i]);
            }
        }
        return mn;
    }
};