【Luogu4735】最大异或和【可持久化01trie】

[ L i n k \frak{Link} Link]


需要支持操作:

  1. 在序列末尾添加数
  2. 给出 l , r , x l,r,x l,r,x
    询问 [ l , r ] [l,r] [l,r] 中满足 a [ p ] ⊕ a [ p + 1 ] ⊕ ⋯ ⊕ a [ N ] ⊕ x a[p]\oplus a[p+1]\oplus\cdots\oplus a[N]\oplus x a[p]a[p+1]a[N]x 最大的位置 p p p 以及这个异或和

对于 [ 1 , n ] [1,n] [1,n] 求使得 a [ p ] ⊕ ⋯ a [ n ] ⊕ x a[p]\oplus\cdots a[n]\oplus x a[p]a[n]x 最大的 x x x 显然可以用 01trie 解决
变成求 p p p 也很简单,多开一棵 01trie 或者干脆用 bool 都可以解决
但是现在要求可以修改,还要求支持区间查询。

你可能发现异或和居然是往后到 N N N 的有点烦人
那么有没有办法让它跟“在序列末尾添加”一样?
有的,只需要用 维护 a[1~N] 的异或和 和 a[1~p-1] 的异或和 来代替就可以了。

接下来我们得到了一个简单的 01trie 问题
然而怎么支持区间查询呢?
根据已有的信息很容易联想到可持久化
至于最大值,只需要在 01trie insert 的时候一路向下标记谁最后来到这里即可。


输出异或和最大值而非 p

关于未定义行为

变量名起清楚一些,不至于把 MAXN 和 MXXN 搞混

注意递归边界……本质上是往下一层的时候要想清楚
一开始直接用这一位的二进制去指导下一位怎么走了


#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<iomanip>
#include<set>
#include<vector>
#include<map>
#include<queue>
using namespace std;
const int MAXN = 6e5 + 10;
const int MXXN = MAXN << 5;
int n, m, Total = 0;
char ch;
int Root[MAXN];
int To[MXXN][2], LatestViewed[MXXN];
bool TempBit[30];
int TempBitTotal;
int InArrayXorSum = 0;
#define CanPass (To[PosRight][TempBit[XorPos]^1] && LatestViewed[To[PosRight][TempBit[XorPos]^1]] >= LeftPos)
void Insert(int Tms, int &Pos, int OldPos, int BitPos)
{
	Pos = ++Total;
	LatestViewed[Pos] = Tms;
	if (BitPos == 25) return;
	bool tmpbt = TempBit[BitPos+1];
	To[Pos][tmpbt^1] = To[OldPos][tmpbt^1];
	Insert(Tms, To[Pos][tmpbt], To[OldPos][tmpbt], BitPos+1);
}
int Query(int LeftPos, int PosRight, int XorPos, int XorAns)
{
	if (XorPos == 26) return XorAns;
	if (CanPass) return Query(LeftPos, To[PosRight][TempBit[XorPos]^1], XorPos + 1, XorAns ^ ((TempBit[XorPos]^1)<<(25-XorPos)));
	return Query(LeftPos, To[PosRight][TempBit[XorPos]], XorPos + 1, XorAns ^ ((TempBit[XorPos])<<(25-XorPos)));
}
int main()
{
	scanf("%d%d", &n, &m);
	for (int a, TempInt, i = 1; i <= n; ++i)
	{
		scanf("%d", &a);
		TempInt = InArrayXorSum; TempBitTotal = 25;
		memset(TempBit, 0, sizeof(TempBit));
		while(TempInt)
		{
			TempBit[TempBitTotal--] = TempInt & 1;
			TempInt >>= 1;
		}
		Insert(i, Root[i], Root[i-1], 0);
		InArrayXorSum ^= a;
	}
	for (int l, r, x, TempInt, i = 1; i <= m; ++i)
	{
		scanf(" %c ", &ch);
		if (ch == 'A')
		{
			++n;
			scanf("%d", &x);
			TempInt = InArrayXorSum; TempBitTotal = 25;
			memset(TempBit, 0, sizeof(TempBit));
			while(TempInt)
			{
				TempBit[TempBitTotal--] = TempInt & 1;
				TempInt >>= 1;
			}
			Insert(n, Root[n], Root[n-1], 0);
			InArrayXorSum ^= x;
		}
		if (ch == 'Q')
		{
			scanf("%d%d%d", &l, &r, &x);
			x ^= InArrayXorSum;
			TempInt = x; TempBitTotal = 25;
			memset(TempBit, 0, sizeof(TempBit));
			while(TempInt)
			{
				TempBit[TempBitTotal--] = TempInt & 1;
				TempInt >>= 1;
			}
			printf("%d\n", Query(l, Root[r], 1, x));
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值