[JZOJ5952] 凯旋而归 ([BZOJ 5092]【Lydsy1711月赛】分割序列)【高维前缀和】【DP】

33 篇文章 0 订阅
29 篇文章 0 订阅

Description

对于一个长度为m的序列a,记 f ( a ) = max ⁡ ( ( a 1   x o r   . . .   x o r   a i ) + ( a i + 1   x o r   . . .   x o r   a n ) ) , i ∈ [ 0 , m ] f(a)=\max((a_1\ xor\ ...\ xor\ a_i)+(a_{i+1}\ xor\ ...\ xor\ a_n)),i\in[0,m] f(a)=max((a1 xor ... xor ai)+(ai+1 xor ... xor an)),i[0,m]

给你一个长度为n的序列a,依次输出 a [ 1..1 ] , a [ 1..2 ] , . . . , a [ 1.. n ] a[1..1],a[1..2],...,a[1..n] a[1..1],a[1..2],...,a[1..n]的f值
a i ≤ 1 0 6 , n ≤ 300000 a_i\leq 10^6,n\leq 300000 ai106,n300000

Solution

我们先将序列a做一次前缀异或,记 b i = a 1   x o r   . . .   x o r   a i b_i=a_1\ xor\ ...\ xor\ a_i bi=a1 xor ... xor ai
那么 f ( a [ 1.. i ] ) = max ⁡ ( b j + ( b i   x o r   b j ) ) , j ∈ [ 0 , i ] f(a[1..i])=\max(b_j+(b_i\ xor\ b_j)),j\in[0,i] f(a[1..i])=max(bj+(bi xor bj)),j[0,i]

从高到低考虑 b i b_i bi的二进制位,如果 2 k 2^k 2k这一位为1,那么j无论怎么选这一位对总和贡献都是 2 k 2^k 2k
如果这一位为0,那么就要看 b j b_j bj的这一位,如果为1那么就有 2 k + 1 2^{k+1} 2k+1的贡献,否则贡献就是0

问题就转化成我们需要选择一个j,使得 b j b_j bj b i b_i bi为0的更的高位要尽可能为1,为1的位可以任意,然后按位确定答案。

问题就在于这个为1的位可以任意。我们很难用一个Trie来维护,因为碰到1需要两边都一起跑。
考虑预处理
预处理状态 F [ S ] F[S] F[S],S为一个二进制状态,一开始S的意义就是S这个二进制数在序列中最早出现的位置,直接扫一遍存进来即可。

为了满足我们为1的位可以任意取,我们修改 F [ S ] F[S] F[S]的定义, F [ S ] F[S] F[S]表示所有二进制数 T T T在序列中最早出现的位置, S   a n d   T = S S\ and\ T=S S and T=S,或者说 S ∈ T S\in T ST,即S的某一位为0实际上T可以为0或1。
这个动态维护比较难,我们先扫一遍存进来,然后做DP,枚举每一个为0的位转移。
本质上,这是一个高维前缀和的过程。

现在查询答案就好查了,我们从高位到低位扫 b i b_i bi,前面已经确定好的不变,然后看这一位是0还是1,是0就判断是否存在 F [ S ′ ] ≤ i , F[S']\leq i, F[S]i,S’前面都满足且这一位为1,然后直接统计即可。

(其实用Trie也是可以的,我们先离线将Trie建出来,然后1的儿子不变,0的儿子合并1的儿子状态,本质与上面相同)

复杂度 O ( ( n + max ⁡ ( a ) ) log ⁡ max ⁡ ( a ) ) O((n+\max(a))\log \max(a)) O((n+max(a))logmax(a))

Code

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <iostream>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define N 460005
using namespace std;
int a[N],n,f[1048576],cf[21],ans[N];
int main()
{
	cin>>n;
	a[0]=0;
	cf[0]=1;
	fo(i,1,20) cf[i]=cf[i-1]<<1;
	memset(f,107,sizeof(f));
	fo(i,1,n) 
	{
		scanf("%d",&a[i]),a[i]^=a[i-1];
		f[a[i]]=min(f[a[i]],i);
	}
	fod(j,cf[20]-1,0) 
		fo(i,0,19) if(!(j&cf[i])) f[j]=min(f[j],f[j^cf[i]]);
	fo(i,1,n)
	{
		int s=0,v=0;
		fod(j,19,0)
		{
			if(a[i]&cf[j]) s+=cf[j];
			else
			{
				v^=cf[j];
				if(f[v]<=i) s+=cf[j+1];
				else v^=cf[j];
			}
		}
		printf("%d\n",s);
	}	
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值