JZOJ5952. 【NOIP2018模拟11.5A组】凯旋而归

15 篇文章 0 订阅
4 篇文章 0 订阅

题意:

数据范围:

对于 50 50 50%的数据, N ≤ 6666 N\leq6666 N6666
对于 100 100 100%的数据, N ≤ 456789 , 0 ≤ a i ≤ 1 0 6 N\leq456789,0\leq a_i \leq10^6 N456789,0ai106

Analysis:

比较有意思的一道题,我们发现答案可以表示成如下形式:
设前缀异或和为 s i s_i si
那么答案就是: max ⁡ \max max{ s i s_i si^ s j + s j s_j+s_j sj+sj}。
我们贪心地取,若当前 s i s_i si位为 1 1 1,发现无论 s j s_j sj这一位是什么,贡献都一样。
我们只用关心二进制位下 s i s_i si 0 0 0的位。
怎么判断是否 s j s_j sj这一位能填 1 1 1呢,发现还要满足前面二进制位的要求,我们不妨预处理李离线。
我们设 f x f_x fx为满足 x x x s i s_i si子集的最小的 i i i。这个可以先赋值,然后倒着扫一下传递一下。
那我们只要确定下之前 s j s_j sj填的二进制位是啥,然后判断对应的 f x f_x fx是否小于等于当前 i i i即可。
复杂度 O ( n log ⁡ ( max ⁡ a i ) ) O(n\log({\max{a_i}})) O(nlog(maxai))

Code:

# include<cstdio>
# include<cstring>
# include<algorithm>
using namespace std;
const int N = 5e5 + 5;
const int M = 1 << 20;
int f[M * 2 + 5],a[N],s[N];
int n;
int main()
{
	freopen("ak.in","r",stdin);
	freopen("ak.out","w",stdout);
	memset(f,0x3f,sizeof(f));
	scanf("%d",&n);
	for (int i = 1 ; i <= n ; ++i) scanf("%d",&a[i]),s[i] = s[i - 1] ^ a[i],f[s[i]] = min(f[s[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 ans = 0,now = 0;
		for (int j = 19 ; ~j ; --j)
		{
			if (s[i] & (1 << j)) { ans += 1 << j; continue; }
			if (f[now ^ (1 << j)] <= i) now += 1 << j,ans += 1 << j + 1;
		}
		printf("%d\n",ans);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值