#线段树,离线#CF1000F One Occurrence
题目
给定一个长度为\(n\)序列,\(m\)个询问,每次询问给定一个区间\([l,r]\),
如果这个区间里存在只出现一次的数,输出这个数(如果有多个就输出任意一个),没有就输出0
分析
考虑离线,枚举右端点\(r\),用线段树维护每个位置的数的上一个出现位置,
线段树维护区间最小值,如果存在一个数出现位置\(<l\)那么输出这个数
代码
#include <cstdio>
#include <cctype>
#include <algorithm>
#define rr register
using namespace std;
const int N=500011,inf=0x3f3f3f3f;
struct rec{int l,r,rk;}q[N];
int a[N],last[N],n,Q,ans[N],pre[N],w[N<<2];
inline signed iut(){
rr int ans=0; rr char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans;
}
inline void print(int ans){
if (ans>9) print(ans/10);
putchar(ans%10+48);
}
bool cmp(rec x,rec y){return x.r<y.r;}
inline void update(int k,int l,int r,int x){
if (l==r) {w[k]=x; return;}
rr int mid=(l+r)>>1;
if (x<=mid) update(k<<1,l,mid,x);
else update(k<<1|1,mid+1,r,x);
w[k]=pre[w[k<<1]]<pre[w[k<<1|1]]?w[k<<1]:w[k<<1|1];
}
inline signed query(int k,int l,int r,int x,int y){
if (l==x&&r==y) return w[k];
rr int mid=(l+r)>>1;
if (y<=mid) return query(k<<1,l,mid,x,y);
else if (x>mid) return query(k<<1|1,mid+1,r,x,y);
else {
rr int t1=query(k<<1,l,mid,x,mid);
rr int t2=query(k<<1|1,mid+1,r,mid+1,y);
return pre[t1]<pre[t2]?t1:t2;
}
}
signed main(){
n=iut(),pre[0]=inf;
for (rr int i=1;i<=n;++i) a[i]=iut();
Q=iut();
for (rr int i=1;i<=Q;++i)
q[i]=(rec){iut(),iut(),i};
sort(q+1,q+1+Q,cmp);
for (rr int i=1,j=1;i<=Q;++i){
for (;j<=q[i].r;++j){
pre[last[a[j]]]=inf;
if (last[a[j]]) update(1,1,n,last[a[j]]);
pre[j]=last[a[j]],update(1,1,n,last[a[j]]=j);
}
rr int t=query(1,1,n,q[i].l,q[i].r);
if (pre[t]<q[i].l) ans[q[i].rk]=a[t];
}
for (rr int i=1;i<=Q;++i) print(ans[i]),putchar(10);
return 0;
}