[SPOJ]MAXOR

Posted StaroForgin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[SPOJ]MAXOR相关的知识,希望对你有一定的参考价值。

MAXOR

题解

分块板子

首先看到它的数据范围限制与询问,应该很容易联想到分块。
很显然,我们可以先处理出来每个整块到每个位置所形成的区间内的最大异或和,询问时只需要匹配以散块边界的异或和即可。
如何快速求出区间的异或和呢?很明显,差分即可。
所以我们求出区间内最大异或和即为我们将区间内的数全部作前缀和后,异或值最大的两个数的异或值。
而求最大异或值明显可以在字典树上匹配,我们只要顺次将区间内的数加入,并且在加入每个数之后都求一下该数对应的最大异或值即可。
我们记 p r e i , j , s u f i , j pre_{i,j},suf_{i,j} prei,j,sufi,j分别表示区间 [ j , R j ] [j,R_{j}] [j,Rj] [ L j , j ] [L_{j},j] [Lj,j]内的最大异或值,我们可以 O ( n 2 S ) O\\left(\\frac{n^2}{S}\\right) O(Sn2)地预处理出来, S S S表示块长。
询问时对于散块,我们可以像预处理整块时那样求出答案。

时间复杂度 O ( n 2 S log ⁡   n + m S log ⁡   n ) ⩾ O ( n 2 m log ⁡   n ) O\\left(\\frac{n^2}{S}\\log\\,n+mS\\log\\,n\\right)\\geqslant O\\left(\\sqrt{n^2m}\\log\\,n\\right) O(Sn2logn+mSlogn)O(n2m logn)
很明显,当 S = n 2 m ≈ 150 S=\\sqrt{\\frac{n^2}{m}}\\approx 150 S=mn2 150时是最优的。

源码

#include<bits/stdc++.h>
using namespace std;
#define MAXN 12005
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
typedef long long LL;
typedef unsigned long long uLL;       
const LL INF=0x3f3f3f3f;       
const int mo=1e9+7;
const int inv2=499122177;
const int jzm=2333;
const int lim=1e9;
const int n1=150;
const int zero=10000;
const int orG=3,invG=332748118;
const double Pi=acos(-1.0);
const double eps=1e-5;
typedef pair<LL,int> pii;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
template<typename _T>
void print(_T x){if(x<0){x=(~x)+1;putchar('-');}if(x>9)print(x/10);putchar(x%10+'0');}
LL gcd(LL a,LL b){return !b?a:gcd(b,a%b);}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1LL)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1LL;}return t;}
const int M=MAXN/n1+5;
int n,m,a[MAXN],root,block[MAXN],L[M],R[M],pre[M][MAXN],suf[M][MAXN];
class TrieTree{
	private:
		int ch[MAXN*35][2],tot;
	public:
		void insert(int &now,int dp,int val){
			if(!now)now=++tot;if(dp<0)return ;
			insert(ch[now][(val>>dp)&1],dp-1,val);
		}
		int query(int now,int dp,int val){
			if(dp<0||!now)return 0;int tp=(val>>dp)&1;
			if(ch[now][tp^1])return query(ch[now][tp^1],dp-1,val)|(1<<dp);
			return query(ch[now][tp],dp-1,val);
		}
		void clear(){for(int i=1;i<=tot;i++)ch[i][0]=ch[i][1]=0;tot=root=0;}
}T;
signed main(){
	read(n);read(m);int lans=0;
	for(int i=1;i<=n;i++)read(a[i]);
	for(int i=1;i<=n;i++)a[i]=a[i-1]^a[i];
	for(int i=1;i<=n;i++)block[i]=(i+n1-1)/n1;
	for(int i=1;i<=n;i++){if(!L[block[i]])L[block[i]]=i;R[block[i]]=i;}
	for(int i=1;i<=block[n];i++){
		for(int j=R[i];j>0;j--)T.insert(root,30,a[j]),pre[i][j]=max(pre[i][j+1],T.query(root,30,a[j-1]));T.clear();
		for(int j=L[i];j<=n;j++)T.insert(root,30,a[j-1]),suf[i][j]=max(suf[i][j-1],T.query(root,30,a[j]));T.clear();
	}
	for(int i=1;i<=m;i++){
		int l,r,x,y;read(x);read(y);
		l=(x+lans%n)%n+1;r=(y+lans%n)%n+1;lans=0;if(l>r)swap(l,r);
		if(block[l]==block[r]){
			for(int j=l;j<=r;j++)
				T.insert(root,30,a[j-1]),lans=max(lans,T.query(root,30,a[j]));
			T.clear();printf("%d\\n",lans);continue;
		}
		int al=block[l]+1,ar=block[r]-1;lans=max(pre[ar][l],suf[al][r]);
		for(int i=l;i<=R[block[l]];i++)T.insert(root,30,a[i-1]);
		for(int i=L[block[r]];i<=r;i++)lans=max(lans,T.query(root,30,a[i]));
		printf("%d\\n",lans);T.clear();
	}
	return 0;
}

谢谢!!!

以上是关于[SPOJ]MAXOR的主要内容,如果未能解决你的问题,请参考以下文章

CS Round#53 E Maxor

为啥我的 SPOJ GCD2 代码在 SPOJ 上出错?

提示内存不足,其它服务不能运行

在 SPOJ 上提交代码会导致运行时错误 (SIGABRT)

SPOJ 10628. SPOJ COT Count on a tree

SPOJ-DQUERY-D-query