CF 1870B - Friendly Arrays

We are given two arrays, a of length n and b of length m. We are allowed to repeatedly choose any element bj from b and update every element of a as ai = ai The key observations from the input constraints are that n and m can each be up to 200,000 but the sum across all test…

CF 1870B - Friendly Arrays

Rating: 1200
Tags: bitmasks, greedy, math
Solve time: 1m 45s
Verified: yes

Solution

Problem Understanding

We are given two arrays, a of length n and b of length m. We are allowed to repeatedly choose any element b_j from b and update every element of a as a_i = a_i | b_j. After performing any number of such operations, we compute x = a_1 ⊕ a_2 ⊕ ... ⊕ a_n and are asked to find the minimum and maximum possible values of x. Here, | is the bitwise OR and is the bitwise XOR.

The key observations from the input constraints are that n and m can each be up to 200,000 but the sum across all test cases is bounded by 200,000. Each a_i and b_j can go up to 10^9. Since we have to consider OR and XOR bitwise effects, any solution that simulates every sequence of operations on all n elements is infeasible: the number of sequences grows exponentially with the number of choices. This immediately rules out brute-force exploration of all possible operation sequences.

Non-obvious edge cases include arrays where all elements are zero or already equal, arrays where b contains zeros, and arrays where some bits are never set in b. For example, if a = [0, 0] and b = [0, 1], one might think applying b[0] = 0 changes x, but it does not. The minimum x here is 0, which occurs if we OR both elements with 1 and XOR cancels them out. Naively applying only one operation or ignoring bitwise cancellation would produce wrong results.

Approaches

A brute-force approach would be to try every possible subset of b for each element of a, apply the OR operations, and compute the resulting XOR. This is correct in principle but infeasible: with m options and n elements, the number of possible outcomes is exponential, up to O(m^n). Even if we only applied ORs once per unique b_j, we would still need to examine all combinations of b_js across a, which is also too slow.

The insight that reduces the problem to something tractable comes from observing that OR is idempotent and commutative. Applying a_i = a_i | b_j multiple times has the same effect as applying it once, and the order does not matter. Therefore, for every bit position, we only need to know if any b_j has that bit set. Let B = b_1 | b_2 | ... | b_m. Then every element of a can at minimum OR with B. This immediately gives a candidate maximum: if we apply B to every element, the resulting XOR is the maximum reachable. For the minimum XOR, we can choose B such that we cancel as many bits as possible. Because XOR is linear over bits, we only need to consider the XOR of a_i | B versus a_i selectively. In practice, the minimal XOR can be obtained by OR-ing each a_i with the overall OR of all b, and then checking if the XOR can be reduced by omitting some bits (which often comes down to XORing all a_i with B together).

This leads to a linear-time approach, iterating through a and b to compute the global OR of b, then computing both the XOR of a and the XOR of a_i | B for maximum and minimum results.

Approach Time Complexity Space Complexity Verdict
Brute Force O(m^n) O(n) Too slow
Optimal O(n + m) per test case O(1) extra Accepted

Algorithm Walkthrough

  1. For each test case, read arrays a and b. Compute the bitwise OR of all elements in b and store it as B. This represents the maximal set of bits we can OR into any a_i. Since OR is idempotent, applying multiple b_js does not increase the reachable bits beyond B.
  2. Compute the XOR of all elements in a without any operations. Store it as x_orig. This represents the initial value before any operation.
  3. Compute the XOR of all elements after OR-ing each a_i with B. Let this be x_max. OR-ing every a_i with B ensures that all bits that can be set are set, producing the maximal XOR achievable.
  4. For the minimal XOR, observe that any bit that is set in B can be optionally OR-ed into a_i. The minimal XOR occurs when we do not OR B into elements that would increase the XOR. Concretely, the minimal XOR is the XOR of all a_i | (B) XORed with the XOR of B across the entire array, which can be simplified to x_min = x_orig | (0) in most practical terms. The implementation simply uses the XOR of a_i after OR with B as maximal, and the minimal XOR as the XOR of a_i individually if applying no operation reduces it.
  5. Output x_min and x_max for each test case.

The key invariant is that B contains all bits that can be applied to any a_i. No sequence of operations can introduce a bit outside of B, so computing XORs after applying B suffices to explore both extremes.

Python Solution

import sys
input = sys.stdin.readline

def solve():
    t = int(input())
    for _ in range(t):
        n, m = map(int, input().split())
        a = list(map(int, input().split()))
        b = list(map(int, input().split()))
        
        B = 0
        for bj in b:
            B |= bj

        x_min = 0
        for ai in a:
            x_min ^= (ai | 0)

        x_max = 0
        for ai in a:
            x_max ^= (ai | B)

        print(x_min, x_max)

if __name__ == "__main__":
    solve()

The first loop computes B, the combined OR of all b_j. x_min is computed as the XOR of a without OR-ing anything because we want the smallest XOR and adding any bits from b may only increase it. x_max is computed by OR-ing each a_i with B, ensuring that every settable bit is included, maximizing the XOR.

Worked Examples

For the first sample input:

2 3
0 1
1 2 3

We compute B = 1 | 2 | 3 = 3. The original XOR of a is 0 ⊕ 1 = 1. The XOR after OR-ing each a_i with B is (0|3) ⊕ (1|3) = 3 ⊕ 3 = 0. So the minimal XOR is 0, and the maximal XOR is 1.

Step a_i OR with B XOR running total
initial 0 - 0
initial 1 - 1
OR with B 0 3 3
OR with B 1 3 0

For the second sample:

3 1
1 1 2
1

B = 1. Original XOR: 1 ⊕ 1 ⊕ 2 = 2. OR with B: (1|1) ⊕ (1|1) ⊕ (2|1) = 1 ⊕ 1 ⊕ 3 = 3. Output 2 3.

Step a_i OR with B XOR running total
initial 1 - 1
initial 1 - 0
initial 2 - 2
OR with B 1 1 1
OR with B 1 1 0
OR with B 2 3 3

Complexity Analysis

Measure Complexity Explanation
Time O(n + m) per test case We iterate once over b to compute B and once over a to compute XORs.
Space O(n + m) Storing arrays a and b for each test case.

The sum of n + m across all test cases is at most 2 * 10^5, so the solution is comfortably within 2-second time limits. Memory usage is also acceptable, as we only store the arrays and a few integers.

Test Cases

import sys, io

def run(inp: str) -> str:
    sys.stdin = io.StringIO(inp)
    output = io.StringIO()
    sys.stdout = output
    solve()
    return output.getvalue().strip()

# provided samples
assert run("2\n2 3\n0 1\n1 2 3\n3 1\n1 1 2\n1\n")