题意:
数据范围:
对于
50
50
50%的数据,
N
≤
6666
N\leq6666
N≤6666
对于
100
100
100%的数据,
N
≤
456789
,
0
≤
a
i
≤
1
0
6
N\leq456789,0\leq a_i \leq10^6
N≤456789,0≤ai≤106
Analysis:
比较有意思的一道题,我们发现答案可以表示成如下形式:
设前缀异或和为
s
i
s_i
si。
那么答案就是:
max
\max
max{
s
i
s_i
si^
s
j
+
s
j
s_j+s_j
sj+sj}。
我们贪心地取,若当前
s
i
s_i
si位为
1
1
1,发现无论
s
j
s_j
sj这一位是什么,贡献都一样。
我们只用关心二进制位下
s
i
s_i
si为
0
0
0的位。
怎么判断是否
s
j
s_j
sj这一位能填
1
1
1呢,发现还要满足前面二进制位的要求,我们不妨预处理李离线。
我们设
f
x
f_x
fx为满足
x
x
x为
s
i
s_i
si子集的最小的
i
i
i。这个可以先赋值,然后倒着扫一下传递一下。
那我们只要确定下之前
s
j
s_j
sj填的二进制位是啥,然后判断对应的
f
x
f_x
fx是否小于等于当前
i
i
i即可。
复杂度
O
(
n
log
(
max
a
i
)
)
O(n\log({\max{a_i}}))
O(nlog(maxai))。
Code:
# include<cstdio>
# include<cstring>
# include<algorithm>
using namespace std;
const int N = 5e5 + 5;
const int M = 1 << 20;
int f[M * 2 + 5],a[N],s[N];
int n;
int main()
{
freopen("ak.in","r",stdin);
freopen("ak.out","w",stdout);
memset(f,0x3f,sizeof(f));
scanf("%d",&n);
for (int i = 1 ; i <= n ; ++i) scanf("%d",&a[i]),s[i] = s[i - 1] ^ a[i],f[s[i]] = min(f[s[i]],i);
for (int i = M - 1 ; ~i ; --i)
{
for (int j = 0 ; j < 20 ; ++j)
if (!(i & (1 << j))) f[i] = min(f[i],f[i | (1 << j)]);
}
for (int i = 1 ; i <= n ; ++i)
{
int ans = 0,now = 0;
for (int j = 19 ; ~j ; --j)
{
if (s[i] & (1 << j)) { ans += 1 << j; continue; }
if (f[now ^ (1 << j)] <= i) now += 1 << j,ans += 1 << j + 1;
}
printf("%d\n",ans);
}
return 0;
}