Fotile模拟赛L

Description

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。
Input

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

Output

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

3 3

1 4 3

0 1

0 1

4 3

Sample Output

5

7

7
HINT

HINT

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

裸的可持久化trie树只能解决单边变化的区间最大异或和问题(参考可持久化trie树模板题)
这个是两边都在变化的,不能用简单可持久化trie树做。

正解是分块+trie树
先预处理一个数组\(f[i][x]\),表示第i块的开头到\(a[x]\)的最大异或和 。
这个用trie树做,相当于是模板题的做法。
然后对于每个询问,前面多出来的部分再暴力计算。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline ll read() {
	char c=getchar();ll a=0,b=1;
	for(;c<'0'||c>'9';c=getchar())if(c=='-')b=-1;
	for(;c>='0'&&c<='9';c=getchar())a=a*10+c-48;return a*b;
}
ll n,m,siz,num,root[12001],tot,l[12001],r[12001];
ll a[12001],f[201][12001],fans,zz[12001],bl[12001];
struct Trie
{
	ll ch[12001*60][2],sum[12001*60];
	void insert(ll pre,ll &x,ll now,ll d)
	{
		x=++tot;sum[x]=sum[pre]+1;if(!d)return ;
		ch[x][0]=ch[pre][0];ch[x][1]=ch[pre][1];
		if(now&(1<<(d-1)))
		{
			insert(ch[pre][1],ch[x][1],now,d-1);
		}
		else
		{
			insert(ch[pre][0],ch[x][0],now,d-1);
		}
	}
	ll query(ll u,ll v,ll now,ll d)
	{
		if(!d)return 0;
		if(now&(1<<(d-1)))
		{
			if(sum[ch[u][0]]-sum[ch[v][0]]!=0)
			{
				return query(ch[u][0],ch[v][0],now,d-1)|(1<<(d-1));
			}
			else
			{
				return query(ch[u][1],ch[v][1],now,d-1);
			}
		}
		else
		{
			if(sum[ch[u][1]]-sum[ch[v][1]]!=0)
			{
				return query(ch[u][1],ch[v][1],now,d-1)|(1<<(d-1));
			}
			else
			{
				return 	query(ch[u][0],ch[v][0],now,d-1);
			}
		}
	}
}tree;
int main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	n=read(),m=read();
	siz=sqrt(n);
	num=n/siz;
	if(n%siz!=0)num++;
	for(ll i=1;i<=n;i++)
	{
		a[i]=read();
		bl[i]=(i-1)/siz+1;a[i]^=a[i-1];
		tree.insert(root[i-1],root[i],a[i],31);
	}
	for(ll i=1;i<=num;i++)
	{
		l[i]=(i-1)*siz+1;r[i]=i*siz;
	}
	r[num]=n;
	for(ll i=1;i<=num;i++)
	{
		for(ll j=l[i]+1;j<=n;j++)
		{
			f[i][j]=tree.query(root[j],root[l[i]-1],a[j],31);
		}
	}
	for(ll i=1;i<=num;i++)
	{
		for(ll j=l[i]+1;j<=n;j++)
		{
			f[i][j]=max(f[i][j],f[i][j-1]);
		}
	}
	ll la,ra;
	for(ll i=1;i<=m;i++)
	{
		la=read(),ra=read();
		ll L=min((la+fans)%n+1,(ra+fans)%n+1);
		ll R=max((la+fans)%n+1,(ra+fans)%n+1);
		ll ret=0;
		ll x=L-1,y=R;
		if(bl[x]==bl[y])
		{
			for(ll i=x;i<y;i++)
			{
				ret=max(ret,tree.query(root[y],root[max(0LL,x-1)],a[i],31));
			}
		}
		else
		{
			ret=f[bl[x]+1][y];
			for(ll i=x;i<=r[bl[x]];i++)
			{
				ret=max(ret,tree.query(root[y],root[max(0LL,x-1)],a[i],31));
			}
		}
		fans=ret;
		cout<<fans<<endl;
	}
	return 0;
}

呵,不难,但是我没写出来。
第一遍写了依托答辩,然后第二遍重构还是看的别人的代码。。
只能说,trie树我是真的写太少了。。
这一块要多谢好多题目了。

然后是分块的运用。
这个分块套dp其实不难理解,但是我想不到。
优雅的暴力啊。。
分块是对能够整合的信息都能够使用的。
这个范围是非常非常大的,多次查询,这个我只能说,菜就多练

trie树。。多写几题吧

posted @ 2024-01-28 21:54  HL_ZZP  阅读(22)  评论(0编辑  收藏  举报