BZOJ2741 【FOTILE模拟赛】L 【可持久化trie + 分块】

题目

FOTILE得到了一个长为N的序列A,为了拯救地球,他希望知道某些区间内的最大的连续XOR和。
即对于一个询问,你需要求出max(Ai xor Ai+1 xor Ai+2 ... xor Aj),其中l<=i<=j<=r。
为了体现在线操作,对于一个询问(x,y):
l = min ( ((x+lastans) mod N)+1 , ((y+lastans) mod N)+1 ).
r = max ( ((x+lastans) mod N)+1 , ((y+lastans) mod N)+1 ).
其中lastans是上次询问的答案,一开始为0。

输入格式

第一行两个整数N和M。
第二行有N个正整数,其中第i个数为Ai,有多余空格。
后M行每行两个数x,y表示一对询问。

输出格式

共M行,第i行一个正整数表示第i个询问的结果。

输入样例

3 3

1 4 3

0 1

0 1

4 3

输出样例

5

7

7

提示

HINT

N=12000,M=6000,x,y,Ai在signed longint范围内。

题解

区间异或和最大,转化为两个前缀和

多次询问不同区间,用可持久化trie树

但每次要任意选出两个数,而常规的trie树只支持一个数询问区间和它的最大异或值,不能处理区间内任意两个数异或和最大值
何破?

我们不可能每次询问\(O(n^2logn)\)枚举其中一个数
那就预处理!
如果我们能预处理出每个区间异或最大值,就是\(O(n^2logn)\)预处理,\(O(1)\)查询

能不能均摊一下?
分块!
我们只预处理每个块头到其后面所有位置的数异或的最大值
具体的,设\(f[i][j]\)表示\(i\)块开头位置到\(j\)中所有数异或的最大值,记块头为\(u\),则\(f[i][j]\)即为区间\([u,j]\)的答案
算出\(f[i][j]\)只需要枚举每个\(j\)就可以了
具体地,\(f[i][j] = max(f[i][j - 1],query(j,区间[u,j - 1]))\)

那么每次询问的时候,对于\(l\)之后的第一个块头\(u\),可以得到出后面的答案\(f[u][r]\)
所以我们只需要计算区间\([l,u - 1]\)的数与其后面的数的最大异或值
这个区间大小不会超过\(\sqrt{n}\),所以可以直接统计

总的复杂度\(O(n\sqrt{n}logn)\)
【坑点,给出的x,y可能超过int范围】

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#define LL long long int
using namespace std;
const int maxn = 12005,Bit = 31,maxm = 6000000,INF = 100000000;
inline LL read(){
	LL out = 0,flag = 1; char c = getchar();
	while (c < 48 || c > 57){if (c == '-') flag = -1; c = getchar();}
	while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
	return out * flag;
}
LL n,m,A[maxn],sum[maxn],bin[40];
LL f[200][maxn],block[maxn],B,lans;
struct trie{
	int ch[maxm][2],sum[maxm],rt[maxn],cnt;
	int ins(int pre,int x){
		int tmp,u;
		tmp = u = ++cnt;
		for (int i = Bit; i >= 0; i--){
			ch[u][0] = ch[pre][0];
			ch[u][1] = ch[pre][1];
			sum[u] = sum[pre] + 1;
			LL t = x & bin[i]; t >>= i;
			pre = ch[pre][t];
			u = ch[u][t] = ++cnt;
		}
		sum[u] = sum[pre] + 1;
		return tmp;
	}
	LL query(int u,int v,int x,int dep){
		if (dep < 0) return 0;
		LL t = x & bin[dep]; t >>= dep;
		if (sum[ch[u][t ^ 1]] - sum[ch[v][t ^ 1]])
			return bin[dep] + query(ch[u][t ^ 1],ch[v][t ^ 1],x,dep - 1);
		return query(ch[u][t],ch[v][t],x,dep - 1);
	}
}T;
int main(){
	bin[0] = 1; for (int i = 1; i <= Bit; i++) bin[i] = bin[i - 1] << 1;
	n = read(); m = read(); B = (int)sqrt(n) + 1;
	n++;
	for (int i = 2; i <= n; i++) A[i] = read();
	for (int i = 1; i <= n; i++){
		sum[i] = sum[i - 1] ^ A[i];
		T.rt[i] = T.ins(T.rt[i - 1],sum[i]);
		block[i] = i / B;
	}
	for (int i = 1; i <= n; i++){
		if (i == 1 || block[i] != block[i - 1]){
			int b = block[i];
			for (int j = i; j <= n; j++){
				f[b][j] = max(f[b][j - 1],T.query(T.rt[j - 1],T.rt[i - 1],sum[j],Bit));
			}
		}
	}
	n--;
	LL l,r,x,y;
	while (m--){
		x = read(); y = read();
		l = min (((x + lans) % n) + 1, ((y + lans) % n) + 1);
		r = max (((x + lans) % n) + 1, ((y + lans) % n) + 1) + 1;
		lans = 0;
		if (block[l] != block[r]) lans = f[block[l] + 1][r];
		for (int i = l; block[i] == block[l] && i < r; i++){
			lans = max(lans,T.query(T.rt[r],T.rt[i],sum[i],Bit));
		}
		printf("%lld\n",lans);
	}
	return 0;
}

posted @ 2018-04-03 21:17  Mychael  阅读(150)  评论(0编辑  收藏  举报