BZOJ5092:[Lydsy1711月賽]分割序列(貪心,高維字首和)
阿新 • • 發佈:2018-12-22
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
51 2 3 4 5
Sample Output
13
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 }