MAXXORSUM - Editorial

PROBLEM LINK:

Practice
Contest: Division 1
Contest: Division 2
Contest: Division 3
Contest: Division 4

Author: agrawall117
Testers: nishant403, satyam_343
Editorialist: iceknight1093

DIFFICULTY:

TBD

PREREQUISITES:

Familiarity with bitwise operations

PROBLEM:

You have two arrays A and B of length N. An array X is said to be good if it satisfies

A_i = X_1 \mid X_2 \mid \ldots \mid X_i \\ B_i = X_i \ \& \ X_{i+1} \ \& \ \ldots \ \&\ X_N \\

for every i from 1 to N.
Find the maximum value of X_1\oplus X_2\oplus\ldots\oplus X_N across all good arrays.

EXPLANATION:

Since everything’s a bitwise operation, let’s look at each bit separately and check whether we can add this bit to our bitwise XOR.

Consider a fixed bit b. We only need to care about whether b is set or not in each A_i and B_i.
So, we can treat A and B as boolean arrays corresponding to this bit.

Now, let’s use the fact that A is prefix OR and B is suffix AND to deduce some properties about which X_i have bit b set.
Since the input guarantees that at least one valid X exists, A and B both look like [0, 0, 0, \ldots, 0, 1, 1,1, \ldots, 1] (i.e, some zeros followed by some ones). Of course, they might have different numbers of ones and zeros.
Now,

  • If B_i = 1, then all of X_i, X_{i+1}, \ldots, X_N must have bit b set.
  • If B_i = 0, then at least one of X_i, \ldots, X_N must have b unset.
  • In particular, if B_i = 0 and B_{i+1} = 1, then X_i must have b unset, and X_{i+1}, \ldots, X_N will all have it set. So, X_i is the last time it’s unset.
  • Similar analysis will tell you that if A_i = 0 and A_{i+1} = 1, then X_{i+1} must have b set; while X_1, X_2, \ldots, X_{i} all have it unset. So, X_i is the first time it’s set.

Let \text{first}_b be the first position i such that X_i has bit b set, and \text{last}_b be the last position where it’s unset. These can be found as described above.

Let’s do a bit of casework:

  • If \text{last}_b+1 = \text{first}_b, then we know that exactly some suffix of X (specifically, the one starting from \text{first}_b) contains bit b. Depending on the length of this suffix (odd/even), the final XOR will either contain bit b or not.
  • If \text{first}_b+1 = \text{last}_b, then the suffix of X starting at \text{last}_b+1 contains bit b, as well as position \text{first}_b. Once again, we know exactly how many occurrences there are, so we can tell uniquely whether the answer contains bit b or not.
  • Otherwise, \text{first}_b \lt \text{last}_b. In this case, the positions between \text{first}_b+1 and \text{last}_b-1 are ‘free’, i.e, for each one we can freely decide whether this X_i contains bit b.
    In particular, we can always make the number of occurrences odd, so we can ensure that the answer will always contain bit b.

Processing a single bit takes \mathcal{O}(N) time, so we have a solution in \mathcal{O}(30\cdot N).

TIME COMPLEXITY:

\mathcal{O}(N) per testcase.

CODE:

Tester's code (C++)
using namespace std;
 
 
/*
------------------------Input Checker----------------------------------
*/
 
long long readInt(long long l,long long r,char endd){
    long long x=0;
    int cnt=0;
    int fi=-1;
    bool is_neg=false;
    while(true){
        char g=getchar();
        if(g=='-'){
            assert(fi==-1);
            is_neg=true;
            continue;
        }
        if('0'<=g && g<='9'){
            x*=10;
            x+=g-'0';
            if(cnt==0){
                fi=g-'0';
            }
            cnt++;
            assert(fi!=0 || cnt==1);
            assert(fi!=0 || is_neg==false);
 
            assert(!(cnt>19 || ( cnt==19 && fi>1) ));
        } else if(g==endd){
            if(is_neg){
                x= -x;
            }
 
            if(!(l <= x && x <= r))
            {
                cerr << l << ' ' << r << ' ' << x << '\n';
                assert(1 == 0);
            }
 
            return x;
        } else {
            assert(false);
        }
    }
}
string readString(int l,int r,char endd){
    string ret="";
    int cnt=0;
    while(true){
        char g=getchar();
        assert(g!=-1);
        if(g==endd){
            break;
        }
        cnt++;
        ret+=g;
    }
    assert(l<=cnt && cnt<=r);
    return ret;
}
long long readIntSp(long long l,long long r){
    return readInt(l,r,' ');
}
long long readIntLn(long long l,long long r){
    return readInt(l,r,'\n');
}
string readStringLn(int l,int r){
    return readString(l,r,'\n');
}
string readStringSp(int l,int r){
    return readString(l,r,' ');
}
 
 
/*
------------------------Main code starts here----------------------------------
*/
 
const int MAX_T = 1e5;
const int MAX_N = 1e5;
const int MAX_SUM_N = 3e5;
const int BIT = 30;
const int MAX_VAL = (1 << BIT);
 
#define fast ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0)
 
int sum_n = 0;
int max_n = 0;
int max_ans = 0;
 
void solve()
{
    int n;
    n = readIntLn(1,MAX_N);
    
    max_n = max(max_n, n);
    sum_n += n;
    assert(sum_n <= MAX_SUM_N);
    
    int a[n];
    for(int i=0;i<n;i++) {
        if(i != n - 1) {
            a[i] = readIntSp(0,MAX_VAL);
        } else {
            a[i] = readIntLn(0,MAX_VAL);
        }
    }
    
    int b[n];
    for(int i=0;i<n;i++) {
        if(i != n - 1) {
            b[i] = readIntSp(0,MAX_VAL);
        } else {
            b[i] = readIntLn(0,MAX_VAL);
        }
    }
    
    int ans = 0;
    
    for(int i=0;i<BIT;i++) {
        int bit_val[n];
        
        // -1 means can be 0/1
        for(int j=0;j<n;j++) {
            bit_val[j] = -1;
        }
        
        bool found_1_a = false;
        
        for(int j=0;j<n;j++) {
            if((a[j] >> i) & 1) {
                if(found_1_a == false) {
                    bit_val[j] = 1;
                    found_1_a = true;
                }
            } else {
                assert(found_1_a == false);
                bit_val[j] = 0;
            }
        }
        
        bool found_0_b = false;
        
        for(int j=n-1;j>=0;j--) {
            if(!((b[j] >> i) & 1)) {
                if(found_0_b == false) {
                    assert(bit_val[j] != 1);
                    found_0_b = true;
                    bit_val[j] = 0;
                }
            } else {
                assert(found_0_b == false);
                assert(bit_val[j] != 0);
                bit_val[j] = 1;
            }
        }
        
        int can_change = 0;
        int cur_xor = 0;
        
        for(int j=0;j<n;j++) {
            if(bit_val[j] == 1) cur_xor ^= 1;
            else if(bit_val[j] == -1) can_change = 1;
        }
        
        if(can_change || cur_xor) ans |= (1 << i);
    }
    
    max_ans = max(max_ans , ans);
    
    cout << ans << '\n';
}
 
signed main()
{
    int t = 1;
    t = readIntLn(1,MAX_T);
    
    for(int i=1;i<=t;i++)
    {     
       solve();
    }
    
    assert(getchar() == -1);
 
    cerr<<"SUCCESS\n";
    cerr<<"Tests : " << t << '\n';
    cerr<<"Sum of lengths : " << sum_n << '\n';
    cerr<<"Maximum length : " << max_n << '\n';
    cerr<<"Maximum answer : " << max_ans << '\n';
}
Editorialist's code (Python)
import sys
input = sys.stdin.readline
for _ in range(int(input())):
	n = int(input())
	a = list(map(int, input().split()))
	b = list(map(int, input().split()))
	first_1 = [-1]*31
	last_1 = [n]*31
	for i in range(n):
		for bit in range(30):
			if a[i] & (1 << bit) and first_1[bit] == -1: first_1[bit] = i
			if b[i] & (1 << bit) and last_1[bit] == n: last_1[bit] = i
	ans = 0
	for i in range(31):
		L, R = first_1[i], last_1[i]
		if L == -1: continue
		if L == R:
			if (n-R)%2: ans += 2**i
		elif L+2 == R:
			if (n-R)%2 == 0: ans += 2**i
		else: ans += 2**i
	print(ans)