[CF1000F] One Occurrence(线段树)

题目:

我是超链接

题意:

不强制在线,询问q个区间内只出现一次的数字,输出这个数字(任意一个)

题解:

一棵线段树就行了,从左往右依次把每个位置的数字&pre的位置加上,线段树的节点表示位置
考虑以i为右节点,左节点为l(很多个)的情况。
我们线段树维护每个节点的pre的min,如果min < l,说明这个数字在l~r中只出现了一次,就是个合法的数字,那么我们区间查询合法数字就行
但是如果这种情况呢?这里写图片描述
2的pre在l的左端,但是3还出现了,就不是个合法的情况
怎么避免呢?
可以选择在加入3的时候,如果他的pre存在(2),就把pre这个位置的值去掉就可以了,设为INF

这样就是一个单点修改,区间查询的线段树了

代码:

#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
#define INF 1e9
const int N=500005;
int ans[N],a[N],pre[N],last[N];
struct hh{int pre,x;hh(int X=0,int Y=0){pre=X; x=Y;}}tree[N*4],hx;
vector<hh>q[N];
bool operator <(hh a,hh b){return a.pre<b.pre;}
void updata(int now){tree[now]=min(tree[now<<1],tree[now<<1|1]);}
void change(int now,int l,int r,int x)
{
    if (l==r){tree[now]=hx;return;}
    int mid=(l+r)>>1;
    if (x<=mid) change(now<<1,l,mid,x);
    else change(now<<1|1,mid+1,r,x); 
    updata(now);
}
void qurry(int now,int l,int r,int lrange,int rrange)
{
    if (lrange<=l && rrange>=r) {hx=min(hx,tree[now]);return;}
    int mid=(l+r)>>1;
    if (lrange<=mid) qurry(now<<1,l,mid,lrange,rrange);
    if (rrange>mid) qurry(now<<1|1,mid+1,r,lrange,rrange);
}
int main()
{
    int n,Q;scanf("%d",&n);
    for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    scanf("%d",&Q);
    for (int i=1;i<=Q;i++)
    {
        int x,y;scanf("%d%d",&x,&y);
        q[y].push_back((hh){x,i});
    }
    for (int i=1;i<=n;i++)
    {
        pre[i]=last[a[i]];
        last[a[i]]=i;
    }
    for (int i=1;i<=n;i++)
    {
        if (pre[i])
          {hx=hh(INF,INF);change(1,1,n,pre[i]);}
        hx=hh(pre[i],a[i]);
        change(1,1,n,i);
        for (int j=0;j<q[i].size();j++)
        {
            hx=hh(INF,INF);
            qurry(1,1,n,q[i][j].pre,i);
            if (hx.pre<q[i][j].pre) ans[q[i][j].x]=hx.x;
            else ans[q[i][j].x]=0;
        }
    }
    for (int i=1;i<=Q;i++) printf("%d\n",ans[i]);
}

猜你喜欢

转载自blog.csdn.net/blue_cuso4/article/details/80849637
one
今日推荐