CF1000F One Occurrence
题意
给定一个长度为\(n\)的序列和\(m\)个询问,每次询问给出\(l\)和\(r\),输出\([l,r]\)内只出现一次的某个数字,若没有则输出\(0\)。\(n,m<=5*10^5\)。
思路
一眼莫队,然而刚开始为了维护中出现一次的数字集合使用了\(set\)或者\(vector\)都会在第\(8\)个测试点上卡死,在值域上直接枚举则会在第\(70\)个测试点上寄掉(???)。
颓了一眼题解发现了值域上分块的做法,受益匪浅。
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn=1000050;
int n,m,K,a[maxn],ed[maxn],bel[maxn],st[maxn];
int ans[maxn],col[maxn],cnt,maxx=-1,minn=2147483640;
int sum[10000];
struct ques{
int l,r,id;
bool operator<(const ques &x)const{
if(bel[l]!=bel[x.l]) return l<x.l;
if(bel[l]&1) return r<x.r;
return r>x.r;
}
}q[maxn];
inline int read(){
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') w=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
s=(s<<1)+(s<<3)+(ch^48);
ch=getchar();
}
return s*w;
}
inline void pre(){
int sq=sqrt(500000);
for(int i=1;i<=sq;++i){
st[i]=n/sq*(i-1)+1;
ed[i]=n/sq*i;
}
ed[sq]=500000;
for(int i=1;i<=sq;++i){
for(int j=st[i];j<=ed[i];++j) bel[j]=i;
}
return;
}
inline void add(int k){
if(col[k]==0) cnt++,sum[bel[k]]++;
col[k]++;
if(col[k]==2) cnt--,sum[bel[k]]--;
return;
}
inline void del(int k){
if(col[k]==2) cnt++,sum[bel[k]]++;
col[k]--;
if(col[k]==0) cnt--,sum[bel[k]]--;
return;
}
inline int finds(){
for(int i=bel[500000];i>=1;--i){
if(!sum[i]) continue;
for(int j=ed[i];j>=st[i];j--) if(col[j]==1) return j;
}
return 0;
}
void solve(){
sort(q+1,q+1+m);
for(int i=1,l=1,r=0;i<=m;++i){
while(l>q[i].l) add(a[--l]);
while(r<q[i].r) add(a[++r]);
while(l<q[i].l) del(a[l++]);
while(r>q[i].r) del(a[r--]);
if(cnt) ans[q[i].id]=finds();
else ans[q[i].id]=0;
}
}
int main(){
n=read();pre();
for(int i=1;i<=n;++i) a[i]=read();
m=read();
for(int i=1;i<=m;++i){
q[i].l=read();q[i].r=read();q[i].id=i;
}
solve();
for(int i=1;i<=m;++i) printf("%d\n",ans[i]);
return 0;
}
/*
5
1 1 2 1 1
10
2 1 3
1 1
2
*/