NKOJ8493 最大连续异或和
Problem
给定一个长度为的非负整数数列
有个询问,询问格式为,表示询问区间内的最大的连续异或和。
即求出 其中
强制在线
Solution
分析题目性质,因为异或操作有自反性,所以可以用前缀异或和来维护一段连续异或和,即异或和
那么此题就被转换成了求区间的最大的
显然这个问题可以用来做
区间内任意两数异或之和最大
^
时间复杂度,爆炸,考虑优化
发现^相当于是从中找一个数异或上最大,考虑可持久化计算,时间复杂度被优化到了,可依然要炸。
那么直接考虑可持久化,暴力枚举,在上找异或最大的,单次询问时间复杂度,有次询问,也要炸。
注意到,考虑将个前缀异或和分块,对每一块进行预处理数组,然后询问时暴力查询不是块的部分,是完整块的直接用事先预处理好的即可。
详细的说呢,就是
从第块开头到第块结尾这一段里任意两个数异或最大值
^
枚举块内元素,枚举的是到块的元素。
发现这里同样可以跟上面一样用可持久化优化,于是时间复杂度变成了
对于每次询问时间复杂度。
预处理空间复杂度,树
#include<bits/stdc++.h>
using namespace std;
const int MAX_N = 15000 + 5;
const int MAX_S = 200 + 5;
typedef long long ll;
int n,m,s,tot,sum[MAX_N];
ll f[MAX_S][MAX_S],lastans,a[MAX_N];
inline int calc(int x){return (x-1)/s+1;}
int root[MAX_N];
struct PTrie{
int num;
int nxt[2];
}Ptrie[MAX_N * 32];
void Pins(ll x,int id){
root[id]=++tot;
int p=root[id],q=root[id-1];
for(int i=30;i>=0;i--){
int c=(x>>i)&1;
for(int j=0;j<2;j++)
Ptrie[p].nxt[j]=Ptrie[q].nxt[j];
Ptrie[p].num=Ptrie[q].num+1;
Ptrie[p].nxt[c]=++tot;
p=Ptrie[p].nxt[c];
q=Ptrie[q].nxt[c];
}
Ptrie[p].num=Ptrie[q].num;
Ptrie[p].num++;
}
ll Pask(ll x,int a,int b){
int q=root[a-1],p=root[b];
ll ans=0;
for(int i=30;i>=0;i--){
int c=(x>>i)&1;
int ps=Ptrie[p].nxt[c^1],qs=Ptrie[q].nxt[c^1];
if(Ptrie[ps].num-Ptrie[qs].num>=1){
ans|=(1ll<<i);
p=ps;
q=qs;
}
else{
p=Ptrie[p].nxt[c];
q=Ptrie[q].nxt[c];
}
}
return ans;
}
ll query(int l,int r){
int kl=calc(l),kr=calc(r);
ll ans=0;
if(kl==kr){
for(int i=l;i<=r;i++){
ans=max(ans,Pask(sum[i],l,i));
}
}
else{
ans=0;
if(kl+1<=kr-1) ans=f[kl+1][kr-1];
for(int i=l;i<=kl*s;i++)
ans=max(ans,Pask(sum[i],l,r));
for(int i=(kr-1)*s+1;i<=r;i++)
ans=max(ans,Pask(sum[i],l,r));
}
return ans;
}
int main(){
// freopen("data.in","r",stdin);
// freopen("data.out","w",stdout);
scanf("%d%d",&n,&m);
s=sqrt(n);
Pins(sum[0],0);
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
sum[i]=sum[i-1]^a[i];
Pins(sum[i],i);
}
for(int i=1;i<=calc(n);i++){
for(int j=i;j<=calc(n);j++){
ll maxn=0;
for(int l=(j-1)*s+1;l<=j*s;l++)
maxn=max(maxn,Pask(sum[l],(i-1)*s+1,l));
f[i][j]=max(f[i][j-1],maxn);
}
}
while(m--){
ll x,y;
scanf("%lld%lld",&x,&y);
ll l=(x+lastans)%n+1;
ll r=(y+lastans)%n+1;
if(l>r) swap(l,r);
lastans=query(l-1,r);
printf("%lld\n",lastans);
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现