题意:
给定一个长度为n序列,m个询问,每次询问给定一个区间[l,r],如果这个区间里存在只出现一次的数,输出这个数(如果有多个就输出任意一个),没有就输出0,n,m<=5*10^5
题解:
- 考虑每个查询区间 维护数字上一个出现的位置 如果上一个出现的位置小于l 那么说明区间内只有一个数
- 但是如果区间有两个相同的数字 可能左边的数字的上一个数字小于l 所以要消去其影响
- 所以只要维护每个数字最右边数字的上一个数字出现的位置即可 可以在线用主席树来维护一个前缀和树 或者离线用线段树
#include<bits/stdc++.h> using namespace std; #define ll long long const int N=5e5+10; struct node{int minn,num;}t[N<<6]; node merge(node a,node b) { if(a.minn<b.minn)return (node){a.minn,a.num}; else return (node){b.minn,b.num}; } int lson[N<<6],rson[N<<6],n,m,last[N],pos[N],a[N],T[N],ncnt; void build(int l,int r,int &pos) { pos=++ncnt; if(l==r){t[pos]=(node){1000000000,0};return ;} int m=(l+r)>>1; build(l,m,lson[pos]);build(m+1,r,rson[pos]); t[pos]=merge(t[lson[pos]],t[rson[pos]]); } void upnode(int x,int v,int num,int l,int r,int pre,int &pos) { pos=++ncnt; lson[pos]=lson[pre]; rson[pos]=rson[pre]; t[pos]=t[pre]; if(l==r){t[pos]=(node){v,num};return ;} int m=(l+r)>>1; if(x<=m)upnode(x,v,num,l,m,lson[pre],lson[pos]); else upnode(x,v,num,m+1,r,rson[pre],rson[pos]); t[pos]=merge(t[lson[pos]],t[rson[pos]]); } node qmin(int L,int R,int l,int r,int pos) { if(L<=l&&r<=R)return t[pos]; node ans=(node){100000000,0}; int m=(l+r)>>1; if(L<=m)ans=merge(ans,qmin(L,R,l,m,lson[pos])); if(R>m) ans=merge(ans,qmin(L,R,m+1,r,rson[pos])); return ans; } int main() { cin>>n;node ans; for(int i=1;i<=n;i++)scanf("%d",&a[i]); for(int i=1;i<=n;i++) { last[i]=pos[a[i]]; pos[a[i]]=i; } build(1,n,T[0]); for(int i=1;i<=n;i++) { T[i]=T[i-1]; upnode(i,last[i],a[i],1,n,T[i],T[i]); if(last[i]) { upnode(last[i],100000000,0,1,n,T[i],T[i]); } } cin>>m;int l,r; while(m--) { scanf("%d%d",&l,&r); ans=qmin(l,r,1,n,T[r]); if(ans.minn<l)printf("%d\n",ans.num); else printf("0\n"); } return 0; }View Code