宇宙人浇花(异或前缀和+trie+思维,最大异或对问题)

题目传送门

1.题目大意:
a序列的n个数异或和为: a 1 a1 a1 xor a 2 a2 a2 xor a 3 a3 a3 xor a 4 a4 a4…xor a n an an
可以对一段连续的 a l . . a r al..ar al..ar加一,操作一次或者不操作,求操作之后的n个数的异或和

2.分析题目:
最后一定是前面一部分不加一,中间一部分加一,后面一部分不加一,注意每部分可能长度为0
维护两个前缀数组:
1. b [ i ] 维 护 前 i 个 a [ i ] + 1 的 异 或 和 b[i]维护前i个a[i]+1的异或和 b[i]ia[i]+1
2. a [ i ] 维 护 前 i 个 a [ i ] 的 异 或 和 a[i]维护前i个a[i]的异或和 a[i]ia[i]

3.再用字典树维护a[n] ^ a[i] ^ b[i]
为什么要字典树维护a[n]^a[i] ^b[i]呢?
想想,首先异或运算是满足交换律和结合律的
因为 a [ i ] a[i] a[i]维护的是原a数组的前缀异或和,a[i]^a[n]剩下的是原a数组的,a[i+1] ^ a[i+2]a[n]
b[i] ^ a[i] ^ a[n]为 原a数组的前i个(a[i]+1)异或和再异或原 数组的后面的n-i个a[i]的异或和
(a[1]+1) ^ (a[2]+1) ^(a[3]+1) ^ … ^(a[i]+1) ^a[i+1] ^a[i+2] ^… ^ a[n]
遍历一遍将所有的a[i]^a[n] ^b[i]放trie上面

4.再用a[i]^b[i]去查询trie上的最大异或对
为什么是a[i]^b[i]去查询呢?

因为答案一定是某一个 a[x] ^ b[x] ^ (a[n] ^ a[i] ^ b[i])
简单证明一下:
通过3已知,(a[n] ^ a[i] ^ b[i])为:
(a[1]+1) ^ (a[2]+1) ^(a[3]+1) ^ … ^(a[i]+1) ^a[i+1] ^a[i+2] ^… ^ a[n]
(这里的a数组是没有前缀处理的原a数组)
此时a[n] ^ a[i] ^ b[i]再异或b[x]^a[x]

就会将索引为1-x的异或变成0((a[i]+1)^(a[i]+1)),再将索引1-x的异或和变成a[1] ^ a[2] a[x]

整个a[x] ^ b[x] ^ (a[n] ^ a[i] ^ b[i])变成:

a[1] ^ a[2] ^ …^ a[x] ^ (a[x+1]+1) ^ (a[x+1]+1) ^ (a[x+2]+1) (a[x+k]+1) ^ a[x+k+1] ^ …a[n]
分成开始说的三段!
接下来就是求最大异或对了!

代码:

#include <bits/stdc++.h>
using namespace std;
#define x first
#define y second
# define rep(i,be,en) for(int i=be;i<=en;i++)
# define pre(i,be,en) for(int i=be;i>=en;i--)
typedef pair<int, int> PII;
#define ll long long
#define endl "\n"
#define LOCAL
#define pb push_back
const int N = 2e5 + 10, INF = 0x3f3f3f3f;
inline int read()
{
	int X = 0;
	bool flag = 1;
	char ch = getchar();
	while (ch < '0' || ch > '9') {
		if (ch == '-') flag = 0;
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		X = (X << 1) + (X << 3) + ch - '0';
		ch = getchar();
	}
	if (flag) return X;
	return ~(X - 1);
}

inline void write(int X)
{
	if (X < 0) {
		X = ~(X - 1);
		putchar('-');
	}
	if (X > 9) write(X / 10);
	putchar(X % 10 + '0');
}

int son[N][3];
int n;
int idx, a[N], b[N];

void insert(int x)
{
	int p = 0, u;
	for (int i = 31; i >= 0; i--)
	{
		u = (x >> i) & 1;
		if (!son[p][u]) son[p][u] = ++idx;
		p = son[p][u];
	}
}


int query(int x)
{
	int p = 0, u;
	int res = 0;
	for (int i = 31; i >= 0; i--)
	{
		u = (x >> i) & 1;
		if (son[p][u ^ 1]) res += 1 << i, p = son[p][u ^ 1];
		else
			p = son[p][u];
	}
	return res;
}



void solve()
{
	n = read();
	rep(i, 1, n)
	{
		a[i] = read();
		b[i] = a[i] + 1;
	}

	for (int i = 1; i <= n; i++)
	{
		a[i] ^= a[i - 1];
		b[i] ^= b[i - 1];
	}

	for (int i = 0; i <= n; i++)
	{
		int x = a[n] ^ a[i] ^ b[i];
		insert(x);
	}
	int ans = 0;
	for (int i = 0; i <= n; i++)
		ans = max(ans, query(b[i] ^ a[i]));
	cout << ans << "\n";
}




int main() {
	ios::sync_with_stdio;
	cin.tie(0);
	cout.tie(0);
	//#ifdef LOCAL
	//freopen("data.in.txt","r",stdin);
	//freopen("data.out.txt","w",stdout);
	//#endif
	int T = 1;
	//scanf("%d",&T);
	while (T--)
	{
		solve();
	}
	return 0;
}

撰文不易,点个赞再走吧!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值