CF1000F One Occurrence
题目大意
给定一个长度为\(n\)序列,\(m\)个询问,每次询问给定一个区间\([l,r]\),如果这个区间里存在只出现一次的数,输出这个数(如果有多个就输出任意一个),没有就输出0,\(n,m<=5*10^5\)
分析
看到区间里存在只出现一次的数,区间去重。那一下子就想到离线。
那,我们如何判断区间内,某个数是否只存在一个呢?我们先定义一个数组pre[i]表示为i上次出现的位置。
我们假设[l,r]区间内有一个数x,其在[l,r]只有自己等价于其上次出现位置<l。
我们考虑用线段树维护对某个值来说其上次出现的位置,同时因为需要知道这个值是什么,因此还需要维护一下对应的位置的值。则[l,r]存在只出现一次的数,等价于区间最小值<l
同时区间去重操作,一定要记得消除上个位置的影响
因此,我们直接按右端点排序。每扫到一个值,将其上次出现的位置赋到当前位置,同时将上次出现位置上的值置为INF。
然后来看代码吧!
Ac_code
#include<bits/stdc++.h>
#define ios ios::sync_with_stdio(false); cin.tie(0), cout.tie(0)
using namespace std;
const int N = 5e5 + 10,INF = 0x3f3f3f3f;
struct Node
{
int l,r;
int mi,val;
}tr[N<<2];
struct Query
{
int l,r,id;
bool operator<(const Query& W)const
{
return r<W.r;
}
}query[N];
int pre[N],a[N];
int ans[N];
int n,m;
void push(Node &u,Node l,Node r)
{
if(l.mi<r.mi)
{
u.mi = l.mi;
u.val = l.val;
}
else
{
u.mi =r.mi;
u.val = r.val;
}
}
void pushup(int u)
{
push(tr[u],tr[u<<1],tr[u<<1|1]);
}
void build(int u,int l,int r)
{
tr[u] = {l,r,INF};
if(l==r) return ;
int mid = l + r >> 1;
build(u<<1,l,mid),build(u<<1|1,mid+1,r);
}
void modify(int u,int x,int k)
{
if(tr[u].l==tr[u].r)
{
tr[u].mi = k;
tr[u].val = a[x];
return ;
}
int mid = tr[u].l + tr[u].r >> 1;
if(x<=mid) modify(u<<1,x,k);
else modify(u<<1|1,x,k);
pushup(u);
}
Node get(int u,int l,int r)
{
if(l<=tr[u].l&&tr[u].r<=r) return tr[u];
int mid = tr[u].l + tr[u].r >> 1;
Node res = {INF,INF,INF,INF};
if(l<=mid) push(res,res,get(u<<1,l,r));
if(r>mid) push(res,res,get(u<<1|1,l,r));
return res;
}
int main()
{
ios;
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
cin>>m;
for(int i=1;i<=m;i++)
{
int l,r;cin>>l>>r;
query[i] = {l,r,i};
}
build(1,1,n);
sort(query+1,query+m+1);
int idx = 1;
for(int i=1;i<=m;i++)
{
while(idx<=query[i].r)
{
if(pre[a[idx]]) modify(1,pre[a[idx]],INF);
modify(1,idx,pre[a[idx]]);
pre[a[idx]] = idx;idx++;
}
auto t = get(1,query[i].l,query[i].r);
if(t.mi<query[i].l) ans[query[i].id] = t.val;
}
for(int i=1;i<=m;i++) cout<<ans[i]<<'\n';
return 0;
}

浙公网安备 33010602011771号