可持久化trie树

基本思想 :

可以发现,如果裸着建,不仅要消耗很多的时间,更是要消耗很多的空间。考虑以i为根的字典树和以(i-1)为跟的字典树的异同。
可以发现,在当前以i为根的字典树上减去a[i],就是(i-1)的字典树了。所以,我们可以将除了a[i]之外的结点都连到(i-1)的树上。
当然,i-1的树也是从i-2的树上引用过来的。
这样之后,会出现一个问题。比如,a[i-1]的值与a[i]的值相同,那么我们到底要不要跟新呢?还是直接将root[i]全部指向root[i-1]?
还是之前的做法。除了a[i]的值,其他的值全部引用前面的,将a[i]跟新root[i]。
这样做完之后,可以发现,如果不加以区分,root[i]和root[i-1]的树一模一样,直接做差之后不就没有了。可是事实上,在【i,i】这给区间内,有一个数的值为a[i]。
所以我们用一个sum数组加以区别,也就是说,如果当前的root的结点是引用前面的,那么sum不变。否则(就是a[i]跟新的部分),sum[root[i]]=sum[[root[i-1]]+1。a[i]这个数的每个位置都是之前的+1。。

void insert(int x ,   int last,int &now)
{
	now = ++ cnt ;
	int p = now ;
	for(int i = 30 ;i >= 0 ;i --)
	 {
	 	int id = (x >> i) & 1 ;
	 	num[p] = num[last] + 1 ;
		son[p][id] = ++ cnt ;
		son[p][id ^ 1] = son[last][id ^ 1] ;
		p = son[p][id] , last = son[last][id] ; 
	 }
	 num[p] = num[last] +  1; 
}

这样我们在判断区间的时候,就可以拿sum[root[r]]-sum[root[l-1]]。如果大于0,说明在root[l-1]之后,被跟新过,所以可以取。如果sum值相同,代表root[r]中的那部分是直接引用的root[l-1]中的那部分,这个区间内没有跟新过,所以无法取
查询的时候贪心的走就行了,

int ask(int x , int l , int r )
{
	int ans = 0 ;
	for(int i = 30 ;i >= 0 ;i --)
	 {
	 	int id = (x >> i) & 1 ;
	 	if(num[son[r][id ^ 1] ]- num[son[l][id ^ 1]]> 0)
	 	 ans += 1 << i , l = son[l][id ^ 1] , r = son[r][id ^ 1] ;
	 	else l = son[l][id] , r = son[r][id] ;
	 }
	 return ans ;
}

最大异或和

#include <bits/stdc++.h>
using namespace std ;
#pragma GCC optimize(3 , "Ofast" , "inline")
const int N = 3e5 + 10 ;
int num[N << 6] , son[N << 6][2] ;
int n , m , a[N << 6] , rt[N << 6];
int cnt ;
void insert(int x ,   int last,int &now)
{
	now = ++ cnt ;
	int p = now ;
	for(int i = 30 ;i >= 0 ;i --)
	 {
	 	int id = (x >> i) & 1 ;
	 	num[p] = num[last] + 1 ;
		son[p][id] = ++ cnt ;
		son[p][id ^ 1] = son[last][id ^ 1] ;
		p = son[p][id] , last = son[last][id] ; 
	 }
	 num[p] = num[last] +  1; 
}
int ask(int x , int l , int r )
{
	int ans = 0 ;
	for(int i = 30 ;i >= 0 ;i --)
	 {
	 	int id = (x >> i) & 1 ;
	 	if(num[son[r][id ^ 1] ]- num[son[l][id ^ 1]]> 0)
	 	 ans += 1 << i , l = son[l][id ^ 1] , r = son[r][id ^ 1] ;
	 	else l = son[l][id] , r = son[r][id] ;
	 }
	 return ans ;
}
int main()
{
	int n , m , x , l , r  ;
	scanf("%d%d" ,&n , &m) ;
	for(int i = 1; i <= n ;i ++)
	 {
	 	scanf("%d" , &x) ;
	 	a[i] = a[i - 1] ^ x ;
	 	insert(a[i] , rt[i - 1] , rt[i]) ;
	 }	
	 char str[5] ;
	 for(int i = 1 ;i <= m ;i ++) 
	  {
	  
	  	scanf("%s" , str) ;
	  	if(str[0] == 'A')
	  	 {
	  	 	scanf("%d" , &x) ;
	  	 	a[n + 1] = a[n] ^ x ;
			n ++ ; 
		    insert(a[n] , rt[n - 1] , rt[n]) ;
		 }
		else 
		 {
		 	scanf("%d%d%d"  , &l , &r , &x) ;
		 	l -- , r -- ;
		 	if(l == 0) 
		 	 printf("%d
"  , max(ask(x ^ a[n] , 0, rt[r]) , x ^ a[n])) ;
		 	else 
		 	 printf("%d
" , ask(x ^ a[n] , rt[l - 1] , rt[r])) ;
		 }
		 
	  }
	return 0 ;
}

每次做题提醒自己:题目到底有没有读懂,有没有分析彻底、算法够不够贪心、暴力够不够优雅。
原文地址:https://www.cnblogs.com/spnooyseed/p/12870874.html