BZOJ5092:[Lydsy1711月赛]分割序列(贪心,高维前缀和)

Description

对于一个长度为n的非负整数序列b_1,b_2,...,b_n,定义这个序列的能量为:f(b)=max{i=0,1,...,n}((b_1 xor b_2 xor...xor b_i)+(b_{i+1} xor b_{i+2} xor...xor b_n))其中xor表示按位异或(XOR),给定一个长度为n的非负整数序列a_1,a_2,...,a_n,请计算a的每个前缀的能量值。

Input

第一行包含一个正整数n(n<=300000),表示序列a的长度。
第二行包含n个非负整数a_1,a_2,...,a_n(0<=a_i<=10^6),依次表示a中每个元素的值。

Output

 包含n行,每行一个整数,即a每个前缀的能量值。

Sample Input

5
1 2 3 4 5

Sample Output

1
3
6
10
9

Solution

题意其实就是对数组的异或前缀和$a[i]$,对每个$i$求出最大$a[j]+(a[j]~xor~a[i])$。

既然是二进制,就考虑按位贪心得出$a[j]$的值。

假设$a[i]$当前一位为$1$,那么$a[j]$的这一位选$0$或者$1$对答案都是一样的。

如果$a[i]$当前一位为$0$,那么$a[j]$就这一位填$1$。这一位填了$1$,后面贪每一位的时候必须包含这一位的$1$,也就是超集。这玩意儿用高维前缀和预处理一下最靠前的超集的位置就好了。

Code

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 #define N (1<<20)
 5 using namespace std;
 6 
 7 int n,a[N],f[N];
 8 
 9 int main()
10 {
11     memset(f,0x7f,sizeof(f));
12     scanf("%d",&n);
13     for (int i=1; i<=n; ++i)
14     {
15         scanf("%d",&a[i]);
16         a[i]^=a[i-1]; f[a[i]]=min(f[a[i]],i);
17     }
18     for (int i=0; i<20; ++i)
19         for (int j=0; j<(1<<20); ++j)
20             if (!(j&(1<<i))) f[j]=min(f[j],f[j^(1<<i)]);
21     for (int i=1; i<=n; ++i)
22     {
23         int ans=0,now=0;
24         for (int j=19; j>=0; --j)
25             if (a[i]&(1<<j)) ans+=(1<<j);
26             else if (f[now^(1<<j)]<=i) ans+=(1<<j)*2, now^=(1<<j);
27         printf("%d\n",ans);
28     }
29 }
posted @ 2018-12-22 15:01  Refun  阅读(284)  评论(0编辑  收藏  举报