Nikitosh the painter has a 1-indexed array
A of
N elements. He wants to find the
maximum value of expression
(
A[l1] ⊕
A[l1 + 1] ⊕
... ⊕
A[r1]) + (
A[l2] ⊕
A[l2 + 1] ⊕
... ⊕
A[r2]) where
1 ≤
l1 ≤
r1 <
l2 ≤
r2 ≤
N.
Here,
x ⊕
y means the
bitwise XOR of
x and
y.
Because Nikitosh is a painter and not a mathematician, you need to help him in this task.
Input
The first line contains one integer N, denoting the number of elements in the array.
The second line contains N space-separated integers, denoting A1, A2, ... , AN.
Output
Output a single integer denoting the maximum possible value of the given expression.
Constraints
- 2 ≤ N ≤ 4*105
- 0 ≤ Ai ≤ 109
Subtasks
- Subtask 1 (40 points) : 2 ≤ N ≤ 104
- Subtask 2 (60 points) : Original constraints
Example
Input:
5
1 2 3 1 2
Output:
6
Explanation
Choose (l1, r1, l2, r2) = (1, 2, 3, 3) or (1, 2, 4, 5) or (3, 3, 4, 5).
----------------------------------------------------------------------editorail------------------------------------------------------------------------------
IFFICULTY:
Medium
PREREQUISITES:
Tries, Properties of Bitwise XOR
PROBLEM:
Given is an array A containing numbers in the range 0 to 109. We have to find l1, r1, l2 and r2 (l1≤r1<l2≤r2) such that (A[l1]⊕A[l1+1]⊕⋯⊕A[r1]) + (A[l2]⊕A[l2+1]⊕⋯⊕A[r2]) is maximised. Here x⊕yrefers to Bitwise XOR of x and y.
EXPLANATION:
Subtask 1
Let's start off by thinking of the simplest algorithm that we can. We we will then optimise it step by step by making certain observations.
So, the simplest thing to do is to implement what the problem says. We can run four loops, one each for l1, r1, l2 and r2. This gives us an O(N4) algorithm which will exceed the time limit for all the subtasks.
How can we optimise this? Let's make an observation. If we know for each i = 1 to N, the maximum value we can get for (A[l1]⊕A[l1+1]⊕⋯⊕A[r1]) and (A[l2]⊕A[l2+1]⊕⋯⊕A[r2]) such that r1≤i and i<l2, then we can tell the answer in O(N) by simply taking the maximum of the sum of the two terms over all i.
What remains now is to calculate efficiently for each i the max (A[l1]⊕A[l1+1]⊕⋯⊕A[r1]) and (A[l2]⊕A[l2+1]⊕⋯⊕A[r2]) such that r1≤i and i<l2. Let's call the two terms lbest[i] and rbest[i]respectively. For calculating lbest[i], we iterate from j = i to 1 and find the j such that val = (A[j]⊕A[j+1]⊕⋯⊕A[i]) is maximised. Then, lbest[i] = max(lbest[i−1],val). Similarly, we can fill the array rbest.
Calculating the arrays lbest and rbest require O(N2) time. After that, the answer can be computed in O(N). For this subtask, an O(N2) solution will pass.
Subtask 2
We need to somehow speed up the calculation of arrays lbest and rbest. Up till now, we haven't utilised any property of XOR operation in our solution. Let's try to optimise our algorithm using the following property:
Let cumul[i] = (A[1]⊕A[2]⊕⋯⊕A[i]).
Then for some j≤i, cumul[j−1]⊕cumul[i] = (A[j]⊕A[j+1]⊕⋯⊕A[i]).
The proof behind this is left to the reader as a simple exercise.
We can now say that lbest[i] = max(lbest[i−1],val) where val = maximum of (cumul[j−1]⊕cumul[i]) for j = 1 to i.
Calculating lbest this way still takes O(N2). For i = 1 to N, we need to somehow optimise finding the index j such that (cumul[j−1]⊕cumul[i]) is maximum. If we can do it in O(logA[i]), then we can compute the array lbest(and analogously, rbest too) in O(NlogAmax).
We can use Tries to get the required complexity. We will keep a trie which holds the binary representation of elements in the array cumul. While processing for i from 1 to N, we will first use the trie to get the value which is maximum of (cumul[j−1]⊕cumul[i]) for 1≤j≤i, and then insert cumul[i] into the trie. This will allow us to calculate lbest in the required complexity. Similarly, we can calculate rbest also.
Inserting into a trie is a standard operation. Let us look at how we do the other operation, i.e., for a value x, finding the value y in the trie such that x⊕y is maximised. Since, the trie stores binary representations of numbers, we first convert x to its binary representation. Then, we can go down the trie and for each bit in the representation of x, we try to find whether bit of opposite value exists or not. It is easy to see why we seek opposite; because 1⊕1 and 0⊕0= 0 while 0⊕1 = 1.
COMPLEXITY:
--------------------------------------------------------editorial----------------------------------------------------------------------------------
#include<iostream>
using namespace std;
#include<bits/stdc++.h>
void calc(int val[])
{
val[0]=1;
for(int i=1;i<=31;i++)
{
val[i]=val[i-1]*2;
}
// cout<<" generation done "<<endl;
return;
}
class node
{
public:
node * ll,*rr;
node()
{
ll=NULL;
rr=NULL;
}
};
int join(node * head,int val[],int num)
{
-
node *var=head;
int ans=0;
for(int i=29;i>=0;i--)
{
//cout<<" insert "<<num<< " i is "<<i<<endl;
if(num&val[i])
{
-
if(head->rr!=NULL)
{
head=head->rr;
-
}
else
{
-
head=head->rr;
}
-
if(var->ll!=NULL)
{
var=var->ll;
ans+=val[i];
}
else
{
var=var->rr;
}
}
-
-
else
{
// cout<<" here "<<endl;
if(head->ll!=NULL)
{
// cout<<" kasdkas"<<endl;
head=head->ll;
-
}
else
{
// cout<<" creating "<<endl;
-
head=head->ll;
}
-
if(var->rr!=NULL)
{
var=var->rr;
ans+=val[i];
}
else
{
var=var->ll;
}
}
}
return ans;
}
int main()
{
int n;
cin>>n;
int arr[n+10];
int val[100];
calc(val);
int right[n+10000];
int left[n+1000];
int till_now=0;
for(int i=0;i<n;i++)
{
-
}
node *head1=
new node
(),*head2=
new node
();
till_now=0;
join(head1,val,0);
//cout<<" o inserted "<<endl;
right[0]=arr[0];
for(int i=0;i<n;i++)
{
till_now =till_now ^ arr[i];
int ans=join(head1,val,till_now);
if(i>0)
right[i]=max(right[i-1],ans);
//cout<<" f "<<ans<<endl;
}
-
// cout<<" fw done "<<endl;
join(head2,val,0);
left[n-1]=arr[n-1];
till_now=0;
for(int i=n-1;i>=0;i--)
{
till_now =till_now ^ arr[i];
int ans=join(head2,val,till_now);
if(i<n-1)
left[i]=max(left[i+1],ans);
// cout<<" b "<<ans<<endl;
}
-
//cout<<" bw done "<<endl;
int ans=0;
for(int i=0;i<n-1;i++)
{
ans=max(ans,right[i]+left[i+1]);
}
-
return 0;
}
O(NlogAmax)
--------------------
---------