【学习笔记】回滚莫队 (CF620F Xors on Segments)

回滚莫队的精髓在于撤销。

本题要记录一个子树最小值,所以要用 vector 存路径,再倒序还原回去。 然后用 l a s t a n s lastans lastans 记录上次询问答案。

注意回收 T r i e Trie Trie 树上的节点。常数会小一些。

时间复杂度 O ( ( n + m ) n l o g n ) O((n+m)\sqrt{n}logn) O((n+m)n logn) 。跑了 1700ms 。

#include <cstdio> #include <bitset> #include <algorithm> #include <iostream> #include <bits/stdc++.h> #include <cstring> #define INF 0x3f3f3f3f #define PII pair<int,int> #define PIII pair<int,PII> #define ll long long #define INF 0x3f3f3f3f using namespace std; const int Maxn=5*1e4; const int Maxm=5*1e3; const int Maxa=1e6; const int Maxk=20; int n,m,block,bl[Maxn+5],a[Maxn+5],xo[Maxa+5],L[Maxn+5],R[Maxn+5]; int res[Maxm+5]; struct qry{ int l,r,id; //第一次发现之前的莫队竟然都打错了,应该是 bl[l]==bl[a.l] //又因为回滚莫队对数据有序性的要求比较高,所以同一块内无序的情况会导致出错 bool operator <(const qry &a) const { return (bl[l]==bl[a.l])?r<a.r:l<a.l; } }q[Maxm+5]; struct Trie_Tree{ int Trie[Maxn*Maxk+5][2]; int _max[Maxn*Maxk+5],_min[Maxn*Maxk+5]; int sz1,sz2; PIII used[Maxn*Maxk+5]; //插入较小值 void Insert1(int x) { int it=1,val=xo[x-1]; for(int i=19;i>=0;i--) { int d=val>>i&1; if(!Trie[it][d]) Trie[it][d]=++sz2,_min[sz2]=INF,_max[sz2]=-INF; it=Trie[it][d]; used[++sz1]=make_pair(it,make_pair(_min[it],_max[it])); _min[it]=min(_min[it],x); _max[it]=max(_max[it],x); } } //插入较大值 void Insert2(int x) { int it=1,val=xo[x]; for(int i=19;i>=0;i--) { int d=val>>i&1; if(!Trie[it][d]) Trie[it][d]=++sz2,_min[sz2]=INF,_max[sz2]=-INF; it=Trie[it][d]; used[++sz1]=make_pair(it,make_pair(_min[it],_max[it])); _min[it]=min(_min[it],x); _max[it]=max(_max[it],x); } } //查询较小值 int Query1(int x) { int it=1,tot=0,val=xo[x]; for(int i=19;i>=0;i--) { int d=val>>i&1; if(_min[Trie[it][d^1]]<=x) { tot+=1<<i; it=Trie[it][d^1]; } else it=Trie[it][d]; } return tot; } //查询较大值 int Query2(int x) { int it=1,tot=0,val=xo[x-1]; for(int i=19;i>=0;i--) { int d=val>>i&1; if(_max[Trie[it][d^1]]>=x) { tot+=1<<i; it=Trie[it][d^1]; } else it=Trie[it][d]; } return tot; } void Clear1() { for(int i=sz1;i>=1;i--) { _min[used[i].first]=used[i].second.first; _max[used[i].first]=used[i].second.second; } sz1=0; } void Clear2() { _min[0]=INF,_max[0]=-INF; for(int i=1;i<=sz2;i++) { Trie[i][0]=Trie[i][1]=0; } sz2=1; // memset(Trie,0,sizeof(Trie)); } }T1,T2; signed main() { // freopen("data.in","r",stdin); // freopen("own.out","w",stdout); scanf("%d%d",&n,&m); block=max(1,(int)sqrt(1ll*n*n/m)); for(int i=1;i<=1e6;i++) xo[i]=xo[i-1]^i; for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<=n;i++) bl[i]=(i-1)/block+1; for(int i=1;i<=bl[n];i++) { L[i]=(i-1)*block+1,R[i]=min(n,i*block); } for(int i=1;i<=m;i++) { scanf("%d%d",&q[i].l,&q[i].r),q[i].id=i; } sort(q+1,q+1+m); int l,r,lastans,ans; for(int i=1;i<=m;i++) { // printf("%d %d\n",q[i].l,q[i].r); int x=bl[q[i].l]; if(x!=bl[q[i-1].l]) { T1.Clear2(); T2.Clear2(); l=R[x]+1,r=R[x],lastans=0; } //对于在同一块内的情况特判 if(bl[q[i].l]==bl[q[i].r]) { ans=0; for(int j=q[i].l;j<=q[i].r;j++) { T1.Insert1(a[j]); T2.Insert2(a[j]); ans=max({ans,T1.Query1(a[j]),T2.Query2(a[j])}); } res[q[i].id]=ans; T1.Clear2(); T2.Clear2(); continue; } while(r<q[i].r) { r++; T1.Insert1(a[r]); T2.Insert2(a[r]); lastans=max({lastans,T1.Query1(a[r]),T2.Query2(a[r])}); } T1.sz1=T2.sz1=0; ans=lastans; while(l>q[i].l) { l--; T1.Insert1(a[l]); T2.Insert2(a[l]); ans=max({ans,T1.Query1(a[l]),T2.Query2(a[l])}); } res[q[i].id]=ans; T1.Clear1(),T2.Clear1(); l=R[x]+1; } for(int i=1;i<=m;i++) { printf("%d\n",res[i]); } }

__EOF__

本文作者仰望星空的蚂蚁
本文链接https://www.cnblogs.com/cqbzly/p/17530266.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   仰望星空的蚂蚁  阅读(7)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
点击右上角即可分享
微信分享提示