[bzoj5092][乱搞]分割序列

本文介绍了一种计算非负整数序列的能量值的方法,通过求前缀异或和,利用Trie树优化计算过程,实现高效求解每个前缀的能量最大值。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

题解

感觉这个套路以前没有玩过啊…
求出前缀异或和,然后就是要计算一个max(s[j]+s[j]∧s[i])max(s[j]+s[j]\land s[i])max(s[j]+s[j]s[i])
不考虑别的,我们还是套路地从高位往低位考虑s[j]s[j]s[j]
对于第i位是1的,显然s[j]的第i位是0是1都只会有2i2^i2i的贡献
对于是0的,我们当然希望s[j]的第i位是1来获得2i+12^{i+1}2i+1的贡献
如果朴素Tire的话我们需要在是1的地方往0和1的孩子搜,0就贪心往1走
也可以离线后建出Tire,把1的孩子并到0的孩子上,1的孩子不变
简单考虑怎么做
设一个F[S]F[S]F[S]表示对于所有SandT=SSandT=SSandT=S即S为T的子集的所有T的最早出现位置
可以用一个DP求出来
仍然从大往小扫,如果当前位是1就直接加上2i2^i2i贡献
否则我们看看之前确定要填1的位置再加上当前要填1的位置组成的SSS中是否有一个TTT出现在当前这个数的左边,是的话就加上2i+12^{i+1}2i+1并记录这里填了1,否则不做操作
因为其他位是否是1都不影响答案,不需要考虑

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<ctime>
#include<map>
#include<bitset>
#include<set>
#define LL long long
#define mp(x,y) make_pair(x,y)
#define pll pair<long long,long long>
#define pii pair<int,int>
using namespace std;
inline LL read()
{
	LL f=1,x=0;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int stack[20];
inline void write(LL x)
{
	if(x<0){putchar('-');x=-x;}
    if(!x){putchar('0');return;}
    int top=0;
    while(x)stack[++top]=x%10,x/=10;
    while(top)putchar(stack[top--]+'0');
}
inline void pr1(int x){write(x);putchar(' ');}
inline void pr2(LL x){write(x);putchar('\n');}
const int MAXN=300005;
const int MAXM=1000005;
int f[8*MAXM],a[MAXN],n;
int bin[25];
int main()
{
//	freopen("a.in","r",stdin);
//	freopen("a.out","w",stdout);
	bin[0]=1;for(int i=1;i<=22;i++)bin[i]=bin[i-1]<<1;
	n=read();
	memset(f,63,sizeof(f));
	for(int i=1;i<=n;i++)a[i]=read()^a[i-1],f[a[i]]=min(f[a[i]],i);
	for(int i=2*MAXM;i>=0;i--)
		for(int j=21;j>=0;j--)if(i&bin[j])f[i^bin[j]]=min(f[i^bin[j]],f[i]);
	pr2(a[1]);
	for(int i=2;i<=n;i++)
	{
		int ans=0,now=0;
		for(int j=21;j>=0;j--)
		{
			if(a[i]&bin[j])ans+=bin[j];
			else if(f[now|bin[j]]<=i)ans+=bin[j]*2,now|=bin[j];
		}
		pr2(ans);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值