BZOJ:5092 [Lydsy1711月賽]分割序列(貪心&高維字首和)
阿新 • • 發佈:2018-11-12
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
題意:對陣列的異或字首和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; }