【学习笔记】回滚莫队 (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]);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值