BZOJ5092 分割序列(贪心)

  设si为该序列的异或前缀和,则显然相当于求Σmax{sj+sj^si} (i=1~n,j=0~i)。从高位到低位考虑,如果该位si为1,无论sj怎么填都是一样的;如果该位si为0,则sj该位应尽量为1。考虑对每个x预处理出满足si&x=x的最小i。这样贪心时就很好办了。这可以类似前缀和(或者说就是dp)的预处理。

  使用类似的方法可以(n+v)log做选两个数使其or最大的问题。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define N 300010
#define M (1<<20)
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
int read()
{
    int x=0,f=1;char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
    while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x*f;
}
int n,a[N],f[M];
int main()
{
#ifndef ONLINE_JUDGE
    freopen("bzoj5092.in","r",stdin);
    freopen("bzoj5092.out","w",stdout);
    const char LL[]="%I64d\n";
#else
    const char LL[]="%lld\n";
#endif
    n=read();
    memset(f,42,sizeof(f));f[0]=0;
    for (int i=1;i<=n;i++) a[i]=a[i-1]^read(),f[a[i]]=min(f[a[i]],i);
    for (int i=M-1;i;i--)
        for (int j=0;j<20;j++)
        if (!(i&(1<<j))) f[i]=min(f[i],f[i|(1<<j)]);
    for (int i=1;i<=n;i++)
    {
        int x=0,y=0;
        for (int j=19;~j;j--)
        if (a[i]&(1<<j)) x+=1<<j;
        else if (f[y|(1<<j)]<=i) x+=2<<j,y+=1<<j;
        printf("%d\n",x);
    }
    return 0;
}

 

posted @ 2018-12-05 18:28  Gloid  阅读(195)  评论(0编辑  收藏  举报