题解:P10690 Fotile 模拟赛 L
啊因为 SB 错误调了一天。这题是 P4735 的可持久化 01trie 用分块优化。本题要求有一定 01trie 基础。如果已完成 P4735 可以直接跳至此题思路部分。
可持久化 01trie
看到异或,可以想到转换成 01trie 解决问题。在查询区间最大异或值的时候,不一定是从全局直接查询,故需要使用 可持久化 01trie 结合前缀异或和。
可持久化 01trie 就是可持久的 01trie,对于每一次的插入操作,只需要对于有影响的节点复制一遍,记录修改后的节点,每一次复制只会增加
查询操作从二进制下最高位开始,设需要查询一个
从
显然对于每一次询问可以暴力的枚举一个
运用上面两个操作,再搭配上前缀异或和可以实现
此题思路
注意到查询是算法的瓶颈,要考虑用某种算法或数据结构维护区间内的答案。因为我非常喜欢暴力数据结构,所以我用分块预处理出部分答案。
设
接下来在每一次的询问中,就只需要计算考虑散块中的答案即可。
此时的时间复杂度是
代码实现
在实现过程中,因为懒得特判各种
ans 一定要清
int n,m;
int point;
int rt[1000086];
int tr[1000086][2];
int cnt[1000086];
int xum[120086];
int id[120086];
int ltp[586];
int rtp[586];
int ask(int l,int r,int v){
int ans=0;
for(int i=63;i>=0;i--){//事实上远到不了2^63
int qwq=(v>>i)&1;
if(cnt[tr[r][not qwq]]>cnt[tr[l][not qwq]]){//若可以走与当前为相反的
ans+=(1<<i);//当前位可以是1
qwq=not qwq;//当前位取反
}
l=tr[l][qwq];
r=tr[r][qwq];//继续考虑后一位
}
return ans;
}
void insert(int l,int r,int v){
for(int i=63;i>=0;i--){//同上
int qwq=(v>>i)&1;
tr[l][not qwq]=tr[r][not qwq];//复制
tr[l][qwq]=++point;//新增节点
cnt[tr[l][qwq]]=cnt[tr[r][qwq]]+1;//记为可以走相反的
l=tr[l][qwq];
r=tr[r][qwq];//考虑后一位
}
return;
}
int ans;
int s[586][100086];
signed main(){
std::cin>>n>>m;
rt[1]=++point;
insert(rt[1],0,0);//01trie 一定要先插入一个0,以后的插入都是在此基础上做修改
int c=sqrt(n);
int len=(n+1)/c;
if(c*c<n+1)//由于向右移了一格,故处理 01trie 时都是 n+1
len++;
id[1]=1;
for(int i=1;i<=n;i++){
id[i+1]=i/c+1;//预处理id
int x;
std::cin>>x;
xum[i+1]=xum[i] xor x;//前缀异或和
rt[i+1]=++point;
insert(rt[i+1],rt[i],xum[i+1]);//插入当前的xum
}
for(int i=1;i<=len;i++){
ltp[i]=rtp[i-1]+1;
rtp[i]=std::min(n+1,ltp[i]+c-1);//预处理每个块的左右端点
}
for(int i=1;i<=len;i++)
for(int j=ltp[i]+1;j<=n+1;j++)
s[i][j]=std::max(s[i][j-1],ask(rt[ltp[i]-1],rt[j],xum[j]));//其中ask是查询sij范围内的最大异或和,也就是上面的M
while(m--){
int x,y;
int l,r;
std::cin>>x>>y;
l=std::min((ans+x)%n+1,(ans+y)%n+1);
r=std::max((ans+x)%n+1,(ans+y)%n+1);
r++;
ans=0;
int st=id[l];
int ed=id[r];
if(st==ed)//同块内暴力
for(int i=l;i<=r;i++)
ans=std::max(ans,ask(rt[l-1],rt[r],xum[i]));
else{
ans=s[st+1][r];//前面处理好了答案
for(int i=l;i<=rtp[st];i++)
ans=std::max(ans,ask(rt[l-1],rt[r],xum[i]));
}
std::cout<<ans<<"\n";
}
return 0;//撒花!
}
如有不足,欢迎指正。