bzoj 3489: A simple rmq problem
Description
给出一个长度为n的序列,给出M个询问:在[l,r]之间找到一个在这个区间里只出现过一次的数,并且要求找的这个数尽可能大。如果找不到这样的数,则直接输出0, 强制在线。
Solution
设 \(pre[i]\) 表示 \(i\) 之前第一个与 \(a[i]\) 相同的位置
\(nxt[i]\) 表示 \(i\) 之后第一个与 \(a[i]\) 相同的位置
满足要求的位置是:
\(l<=i<=r,pre[i]<l,nxt[i]>r\)
可以看成是点 \((i,pre,nxt)\)
用 \(kdtree\) 维护一下包含所有点的区域就行了
#include<bits/stdc++.h>
using namespace std;
template<class T>void gi(T &x){
int f;char c;
for(f=1,c=getchar();c<'0'||c>'9';c=getchar())if(c=='-')f=-1;
for(x=0;c<='9'&&c>='0';c=getchar())x=x*10+(c&15);x*=f;
}
const int N=1e5+10;
int n,Q,nxt[N],pre[N],la[N],a[N],D,rt,ans=0;
struct data{
int a[3],mx[3],mn[3],w,l,r,v;
inline int& operator [](int x){return a[x];}
}t[N];
inline bool operator <(data p,data q){return p[D]<q[D];}
inline void upd(int o){
int l=t[o].l,r=t[o].r;
for(int i=0;i<3;i++)t[o].mn[i]=t[o].mx[i]=t[o][i];
for(int i=0;i<3;i++){
if(l)t[o].mn[i]=min(t[o].mn[i],t[l].mn[i]),
t[o].mx[i]=max(t[o].mx[i],t[l].mx[i]);
if(r)t[o].mn[i]=min(t[o].mn[i],t[r].mn[i]),
t[o].mx[i]=max(t[o].mx[i],t[r].mx[i]);
}
t[o].v=max(t[o].w,max(t[l].v,t[r].v));
}
inline int build(int l,int r){
D=rand()%3;
int mid=(l+r)>>1,o=mid;
nth_element(t+l,t+mid,t+r+1);
if(l<mid)t[o].l=build(l,mid-1);
if(r>mid)t[o].r=build(mid+1,r);
return upd(o),o;
}
int l,r;
inline bool check(int o){
if(t[o].mn[0]>r || t[o].mx[0]<l)return 0;
if(t[o].mn[1]>=l)return 0;
if(t[o].mx[2]<=r)return 0;
return 1;
}
inline void query(int o){
if(!o || !check(o))return ;
if(t[o].v<=ans)return ;
if(t[o][0]>=l && t[o][0]<=r && t[o][1]<l && t[o][2]>r)ans=max(ans,t[o].w);
query(t[o].l);query(t[o].r);
}
int main(){
freopen("pp.in","r",stdin);
freopen("pp.out","w",stdout);
srand(19260817);
cin>>n>>Q;
for(int i=1;i<=n;i++)gi(a[i]),pre[i]=la[a[i]],la[a[i]]=i;
memset(la,0,sizeof(la));
for(int i=n;i>=1;i--)nxt[i]=la[a[i]]?la[a[i]]:n+1,la[a[i]]=i;
for(int i=1;i<=n;i++)t[i][0]=i,t[i][1]=pre[i],t[i][2]=nxt[i],t[i].w=a[i];
rt=build(1,n);
int x,y;
while(Q--){
gi(x);gi(y);
l=(x+ans)%n+1;r=(y+ans)%n+1;if(l>r)swap(l,r);
ans=0;
query(rt);
printf("%d\n",ans);
}
return 0;
}