来自FallDream的博客,未经允许,请勿转载,谢谢。
因为是OJ上的题,就简单点好了。给出一个长度为n的序列,给出M个询问:在[l,r]之间找到一个在这个区间里只出现过一次的数,并且要求找的这个数尽可能大。如果找不到这样的数,则直接输出0。我会采取一些措施强制在线。
N<=100000,M<=200000
对于每一个ai,找到它的上一个和下一个,假设是l和r,那么右区间在[i,r-1],左区间在[l+1,i]时的答案对ai取max。
这个可以线段树套线段树实现。外层表示右区间,内层表示左区间,标记永久化即可。复杂度nlog^2n
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #define ll long long #define N 131072 #define MN 100000 using namespace std; inline int read() { int x = 0 , f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar();} while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();} return x * f; } struct Tree{int l,r,x,val;}T[MN*350]; int n,m,cnt=0,a[MN+5],lastans=0,Last[MN+5],Pre[MN+5],La[MN+5],t[N*2+5],ans=0; inline int Node(int x){return !x?++cnt:x;} void Query(int x,int l,int r,int v) { if(!x||T[x].x<=ans) return; ans=max(ans,T[x].val); if(l==r) {ans=max(ans,T[x].x);return;} int mid=l+r>>1; if(v<=mid) Query(T[x].l,l,mid,v); else Query(T[x].r,mid+1,r,v); } void Modify(int x,int l,int r,int lt,int rt,int v) { T[x].x=max(T[x].x,v); if(l==lt&&r==rt) { T[x].val=max(T[x].val,v); return; } int mid=l+r>>1; if(rt<=mid) Modify(T[x].l=Node(T[x].l),l,mid,lt,rt,v); else if(lt>mid) Modify(T[x].r=Node(T[x].r),mid+1,r,lt,rt,v); else Modify(T[x].l=Node(T[x].l),l,mid,lt,mid,v), Modify(T[x].r=Node(T[x].r),mid+1,r,mid+1,rt,v); } void modify(int l,int r,int lt,int rt,int v) { for(l+=N-1,r+=N+1;l^r^1;l>>=1,r>>=1) { if(~l&1) Modify(t[l+1],1,n,lt,rt,v); if( r&1) Modify(t[r-1],1,n,lt,rt,v); } } int Query(int x,int l) { ans=0; for(x+=N;x;x>>=1) Query(t[x],1,n,l); } int main() { n=read();m=read(); for(int i=1;i<=N<<1;++i) t[i]=++cnt; for(int i=1;i<=n;++i) a[i]=read(); for(int i=1;i<=n;++i) Pre[i]=La[a[i]],La[a[i]]=i; for(int i=1;i<=n;++i) La[i]=n+1; for(int i=n;i;--i) Last[i]=La[a[i]],La[a[i]]=i; for(int i=1;i<=n;++i) modify(i,Last[i]-1,Pre[i]+1,i,a[i]); for(int i=1;i<=m;++i) { int l=(read()+lastans)%n+1,r=(read()+lastans)%n+1; if(l>r) swap(l,r); printf("%d ",(Query(r,l),lastans=ans)); } return 0; }