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

BZOJ:5092 [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

題意:對陣列的異或字首和a[i],對於每個i,求最大a[j]+a[j]^a[i];

思路:從高位到第位貪心(20->0),如果第i位為1,顯然無論選0或者1夠有一樣的貢獻。如果為0,我們優先選1;這次選了1後,後面選的應該包括這個1,即之前選的1的集合的超集。    我們用高維字首和來求每個數的超集的最小位置。如果最小位置小於等於i,說明這意味可以選1。

(這個公式還可以求最小位置ORZ。

(總覺得再CF做過類似的題,而且當時沒做來

#include<bits/stdc++.h>
using namespace std;
const int maxn=1<<20;
int a[maxn],f[maxn];
int main()
{
    memset(f,127,sizeof(f));
    int N; scanf("%d",&N);
    for(int i=1;i<=N;i++){
        scanf("%d",&a[i]);
        a[i]^=a[i-1];
        f[a[i]]
=min(f[a[i]],i); } for(int i=0;i<20;i++){ for(int j=0;j<(1<<20);j++) if(!(j&(1<<i))) f[j]=min(f[j],f[j|(1<<i)]); } for(int i=1;i<=N;i++){ int ans=0,now=0; for(int j=19;j>=0;j--){ if((a[i]>>j)&1) ans+=(1<<j); else if(f[now|(1<<j)]<=i) ans+=(1<<j)*2,now|=(1<<j); } printf("%d\n",ans); } return 0; }