用pre[i]保存前缀异或值,
询问操作就转化为了:
求x ^ pre[n] ^ pre[p-1]
的最大值,其中l-1<=p-1<=r-1
而x^pre[n]
是常数,所以问题实质就是在给定区间中询问区间元素与一个常数异或的最大值。01字典树的经典老活的区间询问版本。
对pre数组每个前缀建01字典树,每个节点保存有多少个这样的前缀,由于每次只用1个数(二进制串)去更新一条新链,所以用线段树合并的思想利用上一个版本的树去建树,这和主席树的思想如出一辙。询问[l,r]区间就用root[r]版本减去root[l-1]版本,由于节点存的是相同前缀的数量,所以是有可减性的。
不过可持久化01trie的实现和主席树还是有一些不同的。
首先:
①0这个数要作为第0个版本插入,主席树的话0版本可以默认为0,直接从1开始
②由于字典树的根节点是不保留信息的,所以我们递归的时候每次是更新孩子节点。
#include<bits/stdc++.h>
using namespace std;
//#pragma GCC optimize(2)
#define ull unsigned long long
#define ll long long
#define pii pair<int, int>
const int maxn = 6e5 + 10;
const ll mod = 998244353;
const ll inf = (ll)4e17+5;
const int INF = 1e9 + 7;
const double pi = acos(-1.0);
ll inv(ll b){
if(b==1)return 1;return(mod-mod/b)*inv(mod%b)%mod;}
inline ll read()
{