cf1000F One Occurrence (线段树)

时间:2023-03-09 18:50:14
cf1000F One Occurrence (线段树)

这题我是离线做的

设i位置的数上次出现的位置是pre[i](如果第一次出现那就是0)

可以想到,用线段树维护一个区间的pre的最小值,如果它小于区间左端点,那这个数就是一个合法的答案

但直接这样做是错的

考虑1,2,3,4,[1,1],5,虽然前一个1的pre在区间外面,但他后面还有一个1啊

所以可以按照询问的右端点排序,推着来维护这个最小值

具体来说,对于i,先把i位置的值改成pre[i],然后如果有pre[i],那把pre[i]位置的值改成inf(一开始都要初始化成inf)

然后再查的话,我查到的就都是这个区间里的最后一次出现的那个数了,就不会有锅

 #include<bits/stdc++.h>
#define pa pair<int,int>
#define CLR(a,x) memset(a,x,sizeof(a))
using namespace std;
typedef long long ll;
const int maxn=5e5+; inline ll rd(){
ll x=;char c=getchar();int neg=;
while(c<''||c>''){if(c=='-') neg=-;c=getchar();}
while(c>=''&&c<='') x=x*+c-'',c=getchar();
return x*neg;
} int N,Q,pre[maxn],A[maxn],tmp[maxn],ans[maxn];
pa mn[maxn<<];
struct Node{
int l,r,i;
}que[maxn]; inline bool cmp(Node a,Node b){return a.r<b.r;}; inline void update(int p){
mn[p]=min(mn[p<<],mn[p<<|]);
} void change(int p,int l,int r,int x,int y){
if(l==r) mn[p]=make_pair(y,x);
else{
int m=l+r>>;
if(x<=m) change(p<<,l,m,x,y);
else change(p<<|,m+,r,x,y);
update(p);
}
} pa query(int p,int l,int r,int x,int y){
if(x<=l&&r<=y) return mn[p];
int m=l+r>>;pa re=make_pair(N+,);
if(x<=m) re=query(p<<,l,m,x,y);
if(y>=m+) re=min(re,query(p<<|,m+,r,x,y));
return re;
} int main(){
int i,j,k;
N=rd();
for(i=;i<=N;i++){
A[i]=rd();
pre[i]=tmp[A[i]],tmp[A[i]]=i;
}Q=rd();
for(i=;i<=Q;i++){
que[i].l=rd(),que[i].r=rd(),que[i].i=i;
}sort(que+,que+Q+,cmp);
CLR(mn,);
for(i=,j=;i<=Q;i++){
for(;j<=que[i].r&&j<=N;j++){
if(pre[j]) change(,,N,pre[j],N+);
change(,,N,j,pre[j]);
}
pa re=query(,,N,que[i].l,que[i].r);
if(re.first<que[i].l) ans[que[i].i]=A[re.second];
}
for(i=;i<=Q;i++) printf("%d\n",ans[i]);
return ;
}