1. 程式人生 > >BZOJ5092:[Lydsy1711月賽]分割序列(貪心,高維字首和)

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 }