大意:给你一个数组,询问一个区间仅出现一次的数。
思路:我们记录每个位置x左边的第一个相同数的位置y,记为这个x的值为y,如果左边没数的话就是0,然后询问的区间必然是满足存在一个 t,t∈[l,r],且 f(t)<l, f(t)就是左边第一个数的位置,如果找到的这个t,那么答案就存在。
我们将询问离线,按r升序排列,用指针扫来单点更新线段树,如果左边有相同的数的话,要把左边也重新更新一下。查询显然就是查线段树区间最小值。
用两个数组储存最小值和下标即可实现上述操作
细节见代码:
#include<bits/stdc++.h>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define LL long long
#define pii pair<int,int>
#define SZ(x) (int)x.size()
#define all(x) x.begin(),x.end()
using namespace std;
LL gcd(LL a, LL b) {return b ? gcd(b, a % b) : a;}
LL lcm(LL a, LL b) {return a / gcd(a, b) * b;}
LL powmod(LL a, LL b, LL MOD) {LL ans = 1; while (b) {if (b % 2)ans = ans * a % MOD; a = a * a % MOD; b /= 2;} return ans;}
const int N = 5e5 + 3;
const LL mod = 1e9 + 7;
struct uzi{
int l,r,i;
bool operator <(const uzi & t)const{
return r<t.r;
}
}p[N];
int n,m,a[N],t[N<<2];
#define mid (l+r>>1)
#define ls o<<1
#define rs o<<1|1
int nex[N<<2];
void build(int o,int l,int r){
if(l==r){
t[o]=1e9;
nex[o]=l;
return ;
}
build(ls,l,mid);
build(rs,mid+1,r);
t[o]=min(t[ls],t[rs]);
nex[o]=nex[ls];
}
int pos[N];
void up(int o,int l,int r,int pos,int d){
if(l==r){
t[o]=d;
nex[o]=l;
return ;
}
if(pos<=mid)up(ls,l,mid,pos,d);
else up(rs,mid+1,r,pos,d);
nex[o]=(t[ls]<t[rs]?nex[ls]:nex[rs]);
t[o]=min(t[ls],t[rs]);
}
pii get(int o,int l,int r,int x,int y){
if(x<=l&&r<=y){return {t[o],nex[o]};}
pii Q={1e9,0};
if(y>mid){
pii X= get(rs,mid+1,r,x,y);
Q=X;
}
if(x<=mid){
pii X=get(ls,l,mid,x,y);
if(X.fi<Q.fi)Q=X;
}
return Q;
}
int ans[N],G[N];
int main() {
ios::sync_with_stdio(false);
cin>>n;
G[0]=1e9;
build(1,1,n);
for(int i=1;i<=n;i++)cin>>a[i],G[i]=1e9;
cin>>m;
for(int i=1;i<=m;i++)cin>>p[i].l>>p[i].r,p[i].i=i;
sort(p+1,p+1+m);
int l=1;
for(int i=1;i<=m;i++){
while(l<=p[i].r){
if(pos[a[l]]){
up(1,1,n,pos[a[l]],l);
}
up(1,1,n,l,pos[a[l]]);
pos[a[l]]=l;
l++;
}
pii res=get(1,1,n,p[i].l,p[i].r);
if(res.fi>=p[i].l)ans[p[i].i]=0;
else ans[p[i].i]=a[res.se];
}
for(int i=1;i<=m;i++)cout<<ans[i]<<'\n';
return 0;
}