BZOJ.5092.[Lydsy1711月赛]分割序列(高维前缀和)

题目链接

\(Description\)

1143196-20181206140650905-598050428.png

\(Solution\)

首先处理\(a_i\)的前缀异或和\(s_i\)。那么在对于序列\(a_1,...,a_n\),在\(i\)位置处分开的价值为:\(s_i+s_i\ ^{\wedge}s_n\)
虽然有个加,但依旧可以考虑按位计算。如果\(s_n\)的第\(k\)位为\(1\),那\(s_i\)的第\(k\)位为\(0\)或是\(1\)贡献都是\(2^k\)(贡献即\(s_i+s_i\ ^{\wedge}s_n\)在第\(k\)位上是否为\(1\));如果\(s_n\)的第\(k\)位为\(0\),那么\(s_i\)\(k\)位为\(0\)则贡献为\(0\),为\(1\)则贡献为\(2*2^k\)

\(n\)就是指我们当前处理的前缀是\(a[1...n]\)。然后从高到低枚举每一位\(k\),如果\(s_n\)在这一位为\(1\),显然答案一定可以得到\(2^k\)的贡献;否则\(s_n\)在这一位为\(0\),我们应尽量让分割位置\(i\)满足\(s_i\)在第\(k\)位为\(1\),也就是找一个\(n\)前面的位置\(i\)满足\(s_i\)在第\(k\)位为\(1\),如果找得到,答案就可以得到\(2^{k+1}\)的贡献,并限制了\(s_i\)的第\(k\)位为\(1\)
继续枚举更低位\(k'\)时,在第二种情况\(s_i\)不仅要满足\(s_i\)在第\(k'\)位为\(1\),还要满足之前的第\(k\)位为\(1\),也就是找是否存在\(s_i\)\(k,k'\)位同时为\(1\)\(n\)前面的位置\(i\)
之后同理。

也就是说我们要求是否存在\(i\leq n\)\(s_i\)的第\(k\)位为\(1\)且前\(k\)位都满足之前的限制(某些位必须为\(1\))。
不妨去求,第\(k\)位为\(1\)且满足限制的最靠前的位置\(i\),判断是否有\(i\leq n\)
因为限制就是某些位必须为\(1\),其它位任意,也就是超集。所以用高维前缀和维护满足某种限制的集合中,最靠前的位置就可以了。
复杂度\(O(2^kk)\)

//6576kb    1748ms
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
//#define gc() getchar()
#define MAXIN 500000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
typedef long long LL;
const int N=(1<<20)+5;

int s[300005],f[N];
char IN[MAXIN],*SS=IN,*TT=IN;

inline int read()
{
    int now=0;register char c=gc();
    for(;!isdigit(c);c=gc());
    for(;isdigit(c);now=now*10+c-'0',c=gc());
    return now;
}

int main()
{
    static int pw[30];
    pw[0]=1;
    for(int i=1; i<=21; ++i) pw[i]=pw[i-1]<<1;

    int n=read(),mx=0;
    memset(f,0x3f,sizeof f);
    for(int i=1,t; i<=n; ++i)
        s[i]=s[i-1]^read(), mx=std::max(mx,s[i]), f[s[i]]=std::min(f[s[i]],i);
    int bit=1;
    while(pw[bit]<=mx) ++bit;
    for(int i=0,lim=1<<bit; i<bit; ++i)
        for(int s=0; s<lim; ++s)
            if(!(s&pw[i])) f[s]=std::min(f[s],f[s|pw[i]]);
    for(int i=1; i<=n; ++i)
    {
        int ans=0,t=0;
        for(int j=bit-1; ~j; --j)
            if(s[i]&pw[j]) ans|=pw[j];
            else if(f[t|pw[j]]<=i) t|=pw[j], ans+=pw[j+1];//+= not |=...
        printf("%d\n",ans);
    }
    return 0;
}

转载于:https://www.cnblogs.com/SovietPower/p/10076422.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值