Bitwise Subsegment Exclusion#13086

Bitwise Subsegment Exclusion (Editorial)

Observation 1: Values of different lengths

Consider a binary sequence of length .

Its value is

val(b)=2^k+\sum_{i=1}^{k} b_i\cdot 2^{k-i}.

Therefore, every binary sequence of length has a value in the range

[2^k,2^{k+1}-1].

Moreover, every integer in this range corresponds to exactly one binary sequence of length .

For example:

  • sequences of length correspond to values ;

  • sequences of length correspond to values 4,5,6,7;

  • sequences of length correspond to values 8,\dots,15.

So the problem is equivalent to finding the smallest integer v\ge 2 whose corresponding binary sequence does not occur as a contiguous subsegment of the query range.

In other words, we need the MEX of the values of all subsegments inside [l,r], starting from .

Observation 2: We only need subsegments of length at most 18

There are

2^{18}=262144

different binary sequences of length .

Consider a query whose length is m\le 200000.

The number of contiguous subsegments of length inside it is at most

\max(0,m-17)\le 199983.

Since

199983 < 262144,

it is impossible for all binary sequences of length to appear.

Therefore, at least one binary sequence of length is always missing.

Hence the answer always has length at most , and therefore

\text{answer}\le 2^{19}-1=524287.

So it is enough to consider only subsegments of lengths from to .

Observation 3: Process queries offline

Suppose we fix the left border .

For every value , define as the minimum ending position of an occurrence of whose starting position is at least .

If no such occurrence exists, let

best[v]=+\infty.

Now consider a query .

A value appears completely inside this query if and only if

best[v]\le R.

Indeed, represents the earliest ending position among all occurrences starting at or after .

Therefore,

v\text{ is missing from }[L,R] \iff best[v]>R.

This gives us an offline solution.

Sort all queries by decreasing .

We maintain a pointer , initially equal to n+1, and move it from right to left.

Whenever we move from L+1 to , we add every subsegment starting at position whose length is at most .

There are at most such subsegments.

For each one, suppose its length is and its binary part has value .

Its complete value is

v=2^k+x.

If the subsegment ends at position R', we update

best[v]=\min(best[v],R').

How do we find the smallest missing value quickly?

For a query [l,r], we need the smallest such that

best[v]>r.

We build a segment tree over all possible values.

Each leaf corresponding to value stores .

For every segment-tree node, we store the maximum inside its interval.

Consider some segment of values.

There exists a missing value inside this segment if and only if at least one of its values satisfies

best[v]>r.

This is equivalent to saying

\max best[v]>r.

Therefore, while searching for the answer:

  • if the maximum value stored in the left child is greater than , then the left child contains a missing value, so we move left;

  • otherwise, every value in the left child appears inside the query, so we move right.

When we reach a leaf, its index is exactly the smallest missing value.

Correctness Proof

We prove that the algorithm returns the correct answer for every query [l,r].

After the sweep reaches L=l, we have processed exactly all starting positions s\ge l.

For every value , is therefore the minimum ending position among all occurrences of starting at some position s\ge l.

Thus, occurs completely inside [l,r] if and only if

best[v]\le r.

Consequently, is missing if and only if

best[v]>r.

The segment tree stores the maximum in every interval.

Hence an interval contains at least one missing value if and only if its stored maximum is greater than .

During the segment-tree walk, we always move to the left child whenever it contains a missing value. Otherwise, we move to the right child.

Therefore, the reached leaf is the smallest value satisfying best[v]>r, which is exactly the required minimum missing value.

Thus, the algorithm is correct.

Complexity

For every array position, we process at most subsegments.

Therefore, the total number of segment-tree updates is

Each update takes O(\log V), where

V=2^{19}.

Each query also performs one segment-tree walk in O(\log V).

Thus, the total time complexity is

O((18n+q)\log V).

The memory complexity is

Since V=2^{19}, this easily fits the limits.

Implementation detail

It is completely fine to use a fixed segment-tree range up to 2^{19}-1.

The implementation may also use

\min(2^{19},4n+20)

as the upper bound.

This is safe as well.

Suppose the answer has length . Then every binary sequence of length k-1 must already occur, otherwise a smaller value would be missing.

There are 2^{k-1} such sequences, so necessarily