<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Block Decomposition on Gian Alingog</title><link>https://gianalingog.github.io/tags/block-decomposition/</link><description>Recent content in Block Decomposition on Gian Alingog</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Mon, 20 Apr 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://gianalingog.github.io/tags/block-decomposition/index.xml" rel="self" type="application/rss+xml"/><item><title>Square Root Decomposition</title><link>https://gianalingog.github.io/p/square-root-decomposition/</link><pubDate>Mon, 20 Apr 2026 00:00:00 +0000</pubDate><guid>https://gianalingog.github.io/p/square-root-decomposition/</guid><description>&lt;h2 id="introduction-to-block-decomposition"&gt;Introduction to Block Decomposition
&lt;/h2&gt;&lt;p&gt;Block decomposition is a technique used to answer range queries efficiently by splitting the whole interval $[1, N]$ into blocks of size $B$, resulting in $\left \lceil \frac{N}{B} \right \rceil$ of these blocks, with the last block possibly being incomplete.&lt;/p&gt;
&lt;h3 id="example-1"&gt;Example 1
&lt;/h3&gt;&lt;p&gt;Let&amp;rsquo;s go over an example problem together.&lt;/p&gt;
&lt;p&gt;Given an integer array $A$ of length $N$, answer $Q$ queries of the form, &amp;ldquo;find the sum of the integers in the range $[l, r]$.&amp;rdquo; Formally, return $\sum_{i=l}^{r} A_i$.
&lt;br&gt;$A_i \leq 10^9$
&lt;br&gt;$N, Q \leq 10^5$&lt;/p&gt;
&lt;p&gt;A straightforward $\Theta(NQ)$ algorithm by iterating through all the elements in the range given by each query. If you are familiar with &lt;a class="link" href="https://gianalingog.github.io/p/prefix-sums/" &gt;prefix sums&lt;/a&gt; to achieve $\Theta(N+Q)$, forget about that for now.&lt;/p&gt;
&lt;h4 id="solution"&gt;Solution
&lt;/h4&gt;&lt;p&gt;What we can do to achieve a better time complexity is block decomposition!&lt;/p&gt;
&lt;h5 id="precomputation"&gt;Precomputation
&lt;/h5&gt;&lt;p&gt;Divide the array into blocks of size $B$ (which we will determine the value of later &amp;mdash; though you can probably guess what it will be). As discussed before this will split the array into $\left \lceil \frac{N}{B} \right \rceil$ blocks of size at most $B$. Then, for each of these blocks, let&amp;rsquo;s precompute the sum of all the elements in this block and denote $\mathrm{sum}(i)$ as the sum for block $i$. This will take $\Theta(N)$ across all blocks.&lt;/p&gt;
&lt;h5 id="handling-queries"&gt;Handling Queries
&lt;/h5&gt;&lt;p&gt;To answer the range queries, we need to iterate over the blocks that have some intersection with $[l, r]$. It&amp;rsquo;s clear that the bound for this is $O(\left \lceil \frac{N}{B} \right \rceil)$. For blocks that are fully contained within $[l, r]$, we can take find the sum of these elements in $\Theta(1)$ each because we precomputed these values previously.&lt;/p&gt;
&lt;p&gt;Then, we just need to handle the blocks that are not fully contained. Convince yourself that there are at most $2$ of these blocks. We can simply iterate over the relevant elements in these blocks in $O(B)$.&lt;/p&gt;
&lt;p&gt;Our runtime is thus $O(B + \left \lceil \frac{N}{B} \right \rceil)$ per query and $O(N + Q(B + \left \lceil \frac{N}{B} \right \rceil))$ overall.&lt;/p&gt;
&lt;h3 id="choosing-the-block-size"&gt;Choosing the Block Size
&lt;/h3&gt;&lt;p&gt;Intuitively, we want to balance the time between the $O(B)$ part and the $O(\left \lceil \frac{N}{B} \right \rceil)$ part of our queries. We can actually just set them equal to each other for the best case:
&lt;/p&gt;
$$ B = \frac{N}{B} $$&lt;p&gt;
&lt;/p&gt;
$$ B^2 = N $$&lt;p&gt;
&lt;/p&gt;
$$ B = \sqrt{N} $$&lt;p&gt;This is how square root decomposition is motivated. We balance the operations between iterating over blocks and iterating over elements in blocks.&lt;/p&gt;
&lt;h4 id="more-complex-block-sizes"&gt;More Complex Block Sizes
&lt;/h4&gt;&lt;p&gt;There are times when we might have a more unbalanced equation, typically when there are $\mathrm{log}$ factors involved.
For example:&lt;/p&gt;
$$ B \mathrm{log}(B) = \frac{N}{B} $$&lt;p&gt;
&lt;/p&gt;
$$ B^2 \mathrm{log}(B) \le B^2 \mathrm{log}(N) = N $$&lt;p&gt;
&lt;/p&gt;
$$ B^2 = \frac{N}{\mathrm{log}(N)} $$&lt;p&gt;
&lt;/p&gt;
$$ B = \sqrt{\frac{N}{\mathrm{log}(N)}} $$&lt;p&gt;We achieve a better runtime by doing so, rather than blindly applying $\sqrt{N}$.&lt;/p&gt;
&lt;h2 id="moving-forward"&gt;Moving Forward
&lt;/h2&gt;&lt;h4 id="other-queries"&gt;Other Queries
&lt;/h4&gt;&lt;h5 id="handling-updates"&gt;Handling Updates
&lt;/h5&gt;&lt;p&gt;Say we also had updates to change the value at an index to a new one. We can update the element in its block in $O(1)$, but that&amp;rsquo;s not the only data that was affected by the update. We also need to update the $\mathrm{sum}(i)$ for the respective block $i$. We can simply recalculate this in $O(\left \lceil \frac{N}{B} \right \rceil)$. This still result in the same runtime as before: $O(N + Q(B + \left \lceil \frac{N}{B} \right \rceil))$.&lt;/p&gt;
&lt;h4 id="resources"&gt;Resources
&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://cp-algorithms.com/data_structures/sqrt_decomposition.html" target="_blank" rel="noopener"
 &gt;CP Algorithms - Sqrt Decomposition&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>