#线段树,离线#CF1000F One Occurrence

题目

给定一个长度为\(n\)序列,\(m\)个询问,每次询问给定一个区间\([l,r]\)

如果这个区间里存在只出现一次的数,输出这个数(如果有多个就输出任意一个),没有就输出0


分析

考虑离线,枚举右端点\(r\),用线段树维护每个位置的数的上一个出现位置,
线段树维护区间最小值,如果存在一个数出现位置\(<l\)那么输出这个数


代码

#include <cstdio>
#include <cctype>
#include <algorithm>
#define rr register
using namespace std;
const int N=500011,inf=0x3f3f3f3f;
struct rec{int l,r,rk;}q[N];
int a[N],last[N],n,Q,ans[N],pre[N],w[N<<2];
inline signed iut(){
	rr int ans=0; rr char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans;
}
inline void print(int ans){
	if (ans>9) print(ans/10);
	putchar(ans%10+48);
}
bool cmp(rec x,rec y){return x.r<y.r;}
inline void update(int k,int l,int r,int x){
	if (l==r) {w[k]=x; return;}
	rr int mid=(l+r)>>1;
	if (x<=mid) update(k<<1,l,mid,x);
	    else update(k<<1|1,mid+1,r,x);
	w[k]=pre[w[k<<1]]<pre[w[k<<1|1]]?w[k<<1]:w[k<<1|1];
}
inline signed query(int k,int l,int r,int x,int y){
	if (l==x&&r==y) return w[k];
	rr int mid=(l+r)>>1;
	if (y<=mid) return query(k<<1,l,mid,x,y);
	else if (x>mid) return query(k<<1|1,mid+1,r,x,y);
	else {
	    rr int t1=query(k<<1,l,mid,x,mid);
	    rr int t2=query(k<<1|1,mid+1,r,mid+1,y);
	    return pre[t1]<pre[t2]?t1:t2;
	}
}
signed main(){
	n=iut(),pre[0]=inf;
	for (rr int i=1;i<=n;++i) a[i]=iut();
	Q=iut();
	for (rr int i=1;i<=Q;++i)
	    q[i]=(rec){iut(),iut(),i};
	sort(q+1,q+1+Q,cmp);
	for (rr int i=1,j=1;i<=Q;++i){
		for (;j<=q[i].r;++j){
			pre[last[a[j]]]=inf;
			if (last[a[j]]) update(1,1,n,last[a[j]]);
			pre[j]=last[a[j]],update(1,1,n,last[a[j]]=j);
		}
		rr int t=query(1,1,n,q[i].l,q[i].r);
		if (pre[t]<q[i].l) ans[q[i].rk]=a[t];
	}
	for (rr int i=1;i<=Q;++i) print(ans[i]),putchar(10);
	return 0;
}
posted @ 2021-05-07 11:05  lemondinosaur  阅读(31)  评论(0编辑  收藏  举报