bzoj 2741: 【FOTILE模拟赛】L(可持久化trie + 分块优化)

时光毁灭记忆、已成空白 提交于 2020-02-01 03:25:12

在这里插入图片描述
在这里插入图片描述


预处理 aa 数组的前缀和,问题就变成在[l - 1,r] 区间内找任意两个数,使得异或值尽可能大。

用可持久化 trie 暴力做 复杂度是O(32nm)O(32 * n * m)

即使去掉 32 这个系数,也可能会被卡常,考虑如何优化。

考虑预处理一部分区间的答案,令 f[l][r]f[l][r] 表示 区间 [l,r][l,r] 的答案,显然不能预处理全部的区间,考虑分块预处理:对 l 分块(或者对 r 分块),f[l][r] 表示 从第 l 个 块的起点到 r 这段区间内 与 a[r] 异或的最大值。g[l][r] 表示 从第 l 个 块的起点到 r 这段区间内的答案。

通过 f[l][r] 和 g[l][r - 1] 可以得到 g[l][r],因此可以边搞边处理。
对于每一次询问[l,r],找到 l 所在块号的右边界 eded,对于 [ed + 1,r] 的答案已经预处理过,枚举 [l,ed] 每一个点,在可持久化 trie 上查询 区间 [l + 1,r] 与 a[l] 异或可得到的最大值,[ed + 1,r]的答案合并即得到[l,r] 的答案。

复杂度:O(30nn+30mn)O(30*n\sqrt n + 30*m*\sqrt n)


代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e4 + 10;
typedef long long ll;
int n,m;
ll a[maxn],f[150][maxn];
struct trie{									//可持久化字典树 
	int sz,son[maxn * 40][2],sum[maxn * 40],root[maxn];
	void init() {
		for(int i = 0; i <= sz; i++)
			son[i][0] = son[i][1] = sum[i] = root[i] = 0;
		sz = 0;
	}
	void upd(int &rt,ll v) {					//类似主席树的更新,每一步都要更新一个结点 
		++sz;
		son[sz][0] = son[rt][0];
		son[sz][1] = son[rt][1];
		sum[sz] = sum[rt] + 1;
		rt = sz;
		int t = rt;
		for(int i = 32; i >= 0; i--) {
			int p = (v >> i) & 1;
			++sz;
			int s = son[t][p];
			sum[sz] = sum[s] + 1;
			son[sz][0] = son[s][0];
			son[sz][1] = son[s][1];
			son[t][p] = sz;
			t = son[t][p];
		}
	}
	ll qry(int l,int r,ll v) {
		ll ans = 0;
		int L = root[max(l - 1,0)],R = root[r];
		for(int i = 32; i >= 0; i--) {
			int p = (v >> i) & 1;
			if(sum[son[R][p ^ 1]] - sum[son[L][p ^ 1]] > 0) {
				L = son[L][p ^ 1],R = son[R][p ^ 1];
				ans |= (1ll << i);
			} else {
				L = son[L][p],R = son[R][p];
			}
		}	
		return ans;
	}
}trie;
int main() {
	scanf("%d%d",&n,&m);
	trie.init();
	a[0] = 0;
	for (int i = 1; i <= n; i++) {
		scanf("%lld",&a[i]);
		a[i] ^= a[i - 1];
		trie.root[i] = trie.root[i - 1];
		trie.upd(trie.root[i],a[i]);
	}
	int block = min(n,120);
	for (int j = 1; (j - 1) * block + 1 <= n; j++) {				//枚举块的起点 
		for (int i = (j - 1) * block + 1; i <= n; i++) {			//枚举终点 
			f[j][i] = trie.qry((j - 1) * block + 1,i,a[i]);
			f[j][i] = max(f[j][i - 1],f[j][i]);
		}
	}
	ll lstans = 0;
	for (ll i = 1,x,y,l,r,nl,p; i <= m; i++) {
		scanf("%lld%lld",&x,&y);
		l = min((x + lstans) % n + 1,(1ll * y + lstans) % n + 1) - 1;
		r = max((x + lstans) % n + 1,(1ll * y + lstans) % n + 1);
		p = l / block + 1 + (l % block > 0);
		nl = (p - 1) * block;				 
		lstans = 0;
		if (nl > r) {
			for (int k = l; k <= r; k++)
				lstans = max(lstans,trie.qry(k,r,a[k]));
		} else {
			lstans = f[p][r];
			for (int k = l; k <= nl; k++)
				lstans = max(lstans,trie.qry(l,r,a[k]));
		}
		printf("%lld\n",lstans);
	}
	return 0;
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!