One Occurrence 线段树求区间只出现过一次的数字

  题意:一个长度为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<<"*******************\n";
    cout<<"pre";
    for(int i=1;i<=11;i++){
        cout<<i<<"->"<<a[i].first<<" ";
    }
    cout<<"\npos";
    for(int i=1;i<=11;i++){
        cout<<i<<"->"<<a[i].second<<" ";
    }
    cout<<"\n*******************\n\n";
}

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\n",ans[i]);
    }
    return 0;
}
/*
6
1 1 2 2 3 3
1
1 3
*/

 

posted @ 2020-01-03 20:20  艾尔夏尔-Layton  阅读(431)  评论(0编辑  收藏  举报