【BZOJ 3489】A Simple RMQ Problem

题目链接

题目描述

询问区间内只出现过一次的数最大值

Sol

直接做不好做 , 我们考虑一个数的可能的对询问的影响

假设对于位置 i i i 上的数 , 它前一个和他相同的数的位置是 p r e i pre_i prei , 后一个和他相同的数的位置是 n x t i nxt_i nxti(没有则各自为 0 0 0 , n + 1 n+1 n+1)
那么显然的 , 当询问的范围满足 L ∈ [ p r e i + 1 , i ] L \in [pre_i+1,i] L[prei+1i] R ∈ [ i , n x t i − 1 ] R \in [i,nxt_i-1] R[inxti1]
该数可能成为答案

这不就是个矩形覆盖吗?
于是来一发二维线段树就可以了
查询相当于是单点询问

复杂度是 O ( ( n + Q ) l o g 2 n ) O((n+Q)log^2n) O((n+Q)log2n)

#include
#include
#include
#include
#include
#include
#include
#define Set(a,b) memset(a,b,sizeof(a))
#define read(a) scanf("%d",&a)
#define readl(a) scanf("%lld",&a)
using namespace std;
int n,m;
const int N=1e5+10;
const int MAXN=2e7;
const int OO=4e5;
struct segment_tree{
	int ls,rs;int Max,tag;
	segment_tree(){ls=rs=Max=tag=0;}
}T[MAXN];int cur;
struct node{int ls,rs,sub,Max;node(){ls=rs=Max=0;}}tr[OO];
int rt;int cnt=0;
int lst[N],a[N],L[N],R[N];
inline void Supdate(int u) {T[u].Max=max(T[u].Max,max(T[T[u].ls].Max,T[T[u].rs].Max));}//标记永久化了 , 不能只用左右儿子转移
void Smodify(int &u,int l,int r,int L,int R,int x){
	if(!u) u=++cur;
	if(l>=L&&r<=R) {T[u].Max=max(T[u].Max,x);T[u].tag=max(T[u].tag,x);return;}
	int mid=l+r>>1;
	if(mid>=R) Smodify(T[u].ls,l,mid,L,R,x);
	else if(mid<L) Smodify(T[u].rs,mid+1,r,L,R,x);
	else Smodify(T[u].ls,l,mid,L,mid,x),Smodify(T[u].rs,mid+1,r,mid+1,R,x);
	return Supdate(u);
}
int ans;
inline void update(int u){tr[u].Max=max(tr[u].Max,max(tr[tr[u].ls].Max,tr[tr[u].rs].Max));}
void Modify(int &u,int l,int r,int L,int R,int LL,int RR,int x)
{
	if(!u) u=++cnt;
	if(l>=L&&r<=R) {Smodify(tr[u].sub,1,n,LL,RR,x);tr[u].Max=max(tr[u].Max,T[tr[u].sub].Max);return;}
	int mid=l+r>>1;
	if(mid>=R) Modify(tr[u].ls,l,mid,L,R,LL,RR,x);
	else if(mid<L) Modify(tr[u].rs,mid+1,r,L,R,LL,RR,x);
	else Modify(tr[u].ls,l,mid,L,mid,LL,RR,x),Modify(tr[u].rs,mid+1,r,mid+1,R,LL,RR,x);
	return update(u);
}
void SQuery(int u,int l,int r,int Y)
{
	if(!u) return;
	ans=max(ans,T[u].tag);
	if(ans>=T[u].Max) return;
	if(l==r) return;
	int mid=l+r>>1;
	if(mid>=Y) return SQuery(T[u].ls,l,mid,Y);
	else       return SQuery(T[u].rs,mid+1,r,Y);
	return;
}
void Query(int u,int l,int r,int X,int Y)
{
	if(!u) return;
	if(ans>=tr[u].Max) return;
	SQuery(tr[u].sub,1,n,Y);//标记永久化后每个点都要询问一次 , 因为是单点询问 , 可以保证复杂度
	if(l==r) return;
	int mid=l+r>>1;
	if(mid>=X) Query(tr[u].ls,l,mid,X,Y);
	else       Query(tr[u].rs,mid+1,r,X,Y);
	return;
}
int main()
{
	read(n);read(m);
	for(int i=1;i<=n;++i){
		read(a[i]);L[i]=lst[a[i]]+1;
		lst[a[i]]=i;
	}
	for(int i=1;i<=n;++i) lst[i]=n+1;
	for(int i=n;i;--i){R[i]=lst[a[i]]-1;lst[a[i]]=i;}
	for(int i=1;i<=n;++i) Modify(rt,1,n,L[i],i,i,R[i],a[i]);
	int lastans=0,x,y,l,r;
	while(m--){
		read(x);read(y);
		l=(x+lastans)%n+1;
		r=(y+lastans)%n+1;
		if(l>r) swap(l,r);
		ans=0;Query(rt,1,n,l,r);
		printf("%d\n",ans);
		lastans=ans;
	}
	return 0;
}

你可能感兴趣的:(线段树,======题解======)