bzoj-2741 L

题意:

给出一个长度为n的序列;

m次查询区间[l,r]子区间的最大异或和;

强制在线;

n<=12000,m<=6000;


题解:
这诡异的数据范围以及诡异的复杂度。。

FOTILE——中国高端数据结构领导者;

如果不考虑区间问题,那么就可以用可持久化Trie树解决;

具体就是求出前缀区间和,插入到可持久化Trie里,然后就是选两个数异或和最大的问题了;

多了一个区间限制之后,如果暴力查,那么是O(n^2logn)的复杂度;

这里进行一下分块,预处理f[i][j]表示从第i块开头,到j这个元素区间的最大异或和是多少;

这个数组大小是n√n,而转移关系是f[i][j]=max(f[i][j-1],s[j]在[i-1,j]之间的最大异或和);

这样预处理的O(n√nlogn),询问时利用一下数组,后面的整段区间直接可以拿出来;

前面零碎的不好超过√n,所以总复杂度O((n+m)√nlogn);

int 是可以过的,注意强制在线那里可能会爆掉;


代码:

#include<math.h>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 12100
#define K 31
using namespace std;
typedef long long ll;
int s[N];
int next[N*K<<1][2],size[N*K<<1],tot;
int root[N];
int f[200][N];
void Insert(int &no,int val,int deep)
{
	int p=++tot;
	next[p][0]=next[no][0],next[p][1]=next[no][1];
	size[p]=size[no]+1;
	no=p;
	if(deep<0)	return ;
	Insert(next[no][!!(val&1<<deep)],val,deep-1);
}
int query(int nol,int nor,int val,int deep)
{
	if(deep<0)	return 0;
	bool index=!(val&1<<deep);
	if(size[next[nor][index]]-size[next[nol][index]])
		return 1<<deep|query(next[nol][index],next[nor][index],val,deep-1);
	else
		return query(next[nol][!index],next[nor][!index],val,deep-1);
}
int calc(int l,int r,int end)
{
	int ret=0;
	for(int i=l;i<end&&i<r;i++)
	{
		ret=max(ret,query(root[i],root[r],s[i],K));
	}
	return ret;
}
int main()
{
	int n,m,i,j,k,l,r,last,bk;
	scanf("%d%d",&n,&m);
	bk=sqrt(n*1.0);
	for(i=1,j=0;i<=n;i++)
	{
		scanf("%d",s+i);
		s[i]^=j;
		root[i]=root[i-1];
		Insert(root[i],s[i],K);
		j=s[i];
	}
	for(i=1;i<=bk+1;i++)
	{
		k=i*bk;
		for(j=k+1;j<=n;j++)
		{
			f[i][j]=max(f[i][j-1],query(root[k-1],root[j],s[j],K));
		}
	}
	for(i=1,last=0;i<=m;i++)
	{
		scanf("%d%d",&l,&r);
		l=(last+l)%n+1,r=(last+r)%n+1;
		if(l>r)	swap(l,r);
		l--;
		j=l%bk==0&&l?l/bk:l/bk+1;
		last=max(calc(l,r,bk*j),f[j][r]);
		printf("%d\n",last);
		last%=n;
	}
	return 0;
}



你可能感兴趣的:(分块,bzoj,可持久化Trie树)