「SP11444 MAXOR」

题目大意

给出一个序列和一些操作,每次查询一段区间内的最大异或子串.

分析

考虑全局最大异或子串怎么做.

\(g_i=a_1\oplus a_2\oplus a_3\oplus\cdots\oplus a_{i-1}\oplus a_i\),考虑用 01trie 维护 \(\{g_i,g_{i+1},\cdots,g_{n-1},g_n\}\),那么这个集合中的数 \(xor\ g_{i-1}\) 后就变成了以 \(i\) 为开头的所有子串的异或值(这是 01trie 的基本操作),这样就可以做到 \(\mathcal{O}(n\log_2v)\)(其中 \(v\) 指值域)查询整个区间的最大异或子串.

继续考虑区间查询,很显然建可持久化 01trie 后可以做到 \(\mathcal{O}(n\log_2v)\) 的复杂度单次查询,但这显然是不行的,考虑继续优化.

发现这个东西很显然可以分块,那么记录一下 \(f_{i,j}\) 表示第 \(i\) 块的左端点到 \(j\) 这段区间内的最大异或子串,那么就只查询的时候就只需要查询左端点在 \(left\sim r_{id_{left}}\)(\(r_i\) 表示第 \(i\) 块的左端点位置, \(id_i\) 表示 \(i\) 所属块的编号),右端点在 \(left\sim right\) 范围内的最大值,直接在可持久化 01trie 上查询就好了.

最后的时间复杂度是 \(\mathcal{O}(n\sqrt{n}\log_2v)\),可能有点卡常,不建议对于左右不完整的块都暴力查询,可能会 TLE.

代码

#include
#define REP(i,first,last) for(register int i=first;i<=last;++i)
#define DOW(i,first,last) for(register int i=first;i>=last;--i)
using namespace std;
inline int read()
{
	int x(0);
	char c(getchar());
	while(c<'0'||c>'9')
	{
		c=getchar();
	}
	while(c>='0'&&c<='9')
	{
		x=(x<<1)+(x<<3)+c-'0';
		c=getchar();
	}
	return x;
}
const int MAXN=1e5+5;
const int MAX_SQRTN=505;
int n,m;
int len,len_,root;
int arr[MAXN];
int id[MAXN];
int l[MAX_SQRTN],r[MAX_SQRTN];
int l_root[MAXN];
int l_xor[MAXN];
int f[MAX_SQRTN][MAXN];
struct Trie//01trie不是重点,不会的可以自行学习
{
	int son[2];
	int sum;
}trie[MAXN*1024];
int trie_cnt=0;
#define SON(n) trie[now].son[n]
#define NEW_SON(n) trie[new_tree].son[n]
inline void PushUp(int now)//合并信息
{
	trie[now].sum=trie[SON(0)].sum+trie[SON(1)].sum;
}
void Insert(int num,int &now,int deep=30)//普通01trie的插入节点部分
{
	if(!now)
	{
		now=++trie_cnt;
	}
	if(deep==-1)
	{
		trie[now].sum++;
		return;
	}
	bool p=(bool)(num&(1<r)
		{
			swap(l,r);
		}
		printf("%d\n",last_answer=MaXor(l,r));
	}
	return 0;
}

你可能感兴趣的:(「SP11444 MAXOR」)