题意:一个长度为n的序列(记为A[i]),q次查询,每次输出查询区间内任意一个只出现一次的数字,没有则输出0。
思路:线段树结点存元素的位置和上一个相同元素出现过的位置(没有则为0,记为pos),线段树维护区间结点最小值,结点封装在pair里,第一key值为前一个相同元素出现的位置,先将查询存下来,对于1到n的每一个前缀【1,r】,维护元素的最右边出现的pos值,例如1 1 2 3 2这个区间,他们的pos值分别是inf,1,inf,0,3。对于已经知道了【1,r】,要得到【1,r+1】,若A【r+1】在前面出现过,则将前面元素的pos值改为inf,再记录自己的pos值。
像1 1 2 3 2 加进来一个3 ,则pos值的变化为inf,1,inf,0,3—> inf,1,inf,inf,4。这个用线段树的单点更新维护,再将询问以右端点排序,从1开始枚举序列的右端点,若此时的右端点和询问的右端点一致,区间查询一次询问的区间,若得到的pos最小值比l小,代表这个数的上一个值出现的位置再区间外边,则这个数就是查询的答案了,若最小值不符合,则代表没有符合题意的数字。
AC代码:复杂度nlogn
#include<iostream> #include<algorithm> #include<stdio.h> using namespace std; const int maxn=5e5+10,inf=0x3f3f3f3f; typedef pair<int,int> pa; int n,q; int A[maxn],ans[maxn],pos[maxn]; pa a[maxn<<2]; struct Q{ int l,r,id; }p[maxn]; void update(int pos,int val,int l,int r,int rt){ if(l==r){ a[rt].first=val; a[rt].second=pos; return; } int m=l+r>>1; if(pos<=m) update(pos,val,l,m,rt<<1); else update(pos,val,m+1,r,rt<<1|1); a[rt]=min(a[rt<<1],a[rt<<1|1]); } pa query(int L,int R,int l,int r,int rt){ if(L<=l&&r<=R){ return a[rt]; } int m=l+r>>1; pa z;z.first=inf; if(L<=m) z=min(z,query(L,R,l,m,rt<<1)); if(R>m) z=min(z,query(L,R,m+1,r,rt<<1|1)); return z; } void check(){ cout<<"******************* "; cout<<"pre"; for(int i=1;i<=11;i++){ cout<<i<<"->"<<a[i].first<<" "; } cout<<" pos"; for(int i=1;i<=11;i++){ cout<<i<<"->"<<a[i].second<<" "; } cout<<" ******************* "; } bool cmp(Q a,Q b){ return a.r==b.r ? a.l<b.l : a.r < b.r; } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d",&A[i]); } cin>>q; for(int i=1;i<=q;i++){ scanf("%d%d",&p[i].l,&p[i].r); p[i].id=i; } sort(p+1,p+1+q,cmp); for(int i=1,j=1;i<=n;i++){ if(pos[A[i]]) update(pos[A[i]],inf,1,n,1); update(i,pos[A[i]],1,n,1); //cout<<"i为"<<i<<endl; //check(); for(;p[j].r==i;j++){ pa now=query(p[j].l,p[j].r,1,n,1); if(now.first<p[j].l){ //cout<<"caonima"<<now.second<<endl; ans[p[j].id]=A[now.second]; } } pos[A[i]]=i; } for(int i=1;i<=q;i++){ printf("%d ",ans[i]); } return 0; } /* 6 1 1 2 2 3 3 1 1 3 */