【CF1000F】One Occurrence

题目

题目链接:https://codeforces.com/contest/1000/problem/F
给定一个长度为 \(n\) 序列,\(Q\) 个询问,每次询问给定一个区间 \([l,r]\),如果这个区间里存在只出现一次的数,输出这个数(如果有多个就输出任意一个),没有就输出 \(0\)
\(n,Q\leq 5\times 10^5\)

思路

预处理出 \(\text{pre}[i]\) 表示下标为 \(i\) 的位置前一个和它相同的数是哪一个。
把询问按照 \(r\) 从小到大排序,当确定右端点为 \(r\) 的时候,我们只需要维护每一个数字最后出现的位置(且不超过 \(r\))的最大值即可。线段树一个区间 \([l,r]\) 维护目前已经加入的数中 \(\text{pre}[x]\in [l,r]\)\(x\)。询问区间 \([1,l)\) 的最大值判断是否不小于 \(l\) 即可。
还需要注意对于 \(\text{pre}[x]=0\) 的位置 \(x\),可以维护一个 set 来求下标最大值。
当插入一个下标 \(i\) 的时候需要取消 \(\text{pre}[i]\) 的贡献。
时间复杂度 \(O(Q\log n)\)

代码

#include <bits/stdc++.h>
using namespace std;

const int N=500010;
int n,Q,a[N],ans[N],pre[N],last[N];
set<int> s;

struct node
{
	int l,r,id;
}b[N];

bool cmp(node x,node y)
{
	return x.r<y.r;
}

struct SegTree
{
	int maxn[N*4];
	
	void update(int x,int l,int r,int k,int v)
	{
		if (l==r) { maxn[x]=v; return; }
		int mid=(l+r)>>1;
		if (k<=mid) update(x*2,l,mid,k,v);
			else update(x*2+1,mid+1,r,k,v);
		maxn[x]=max(maxn[x*2],maxn[x*2+1]);
	}
	
	int query(int x,int l,int r,int ql,int qr)
	{
		if (ql>qr) return 0;
		if (ql<=l && qr>=r) return maxn[x];
		int mid=(l+r)>>1,res=0;
		if (ql<=mid) res=max(res,query(x*2,l,mid,ql,qr));
		if (qr>mid) res=max(res,query(x*2+1,mid+1,r,ql,qr));
		return res;
	}
}seg;

int main()
{
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		pre[i]=last[a[i]]; last[a[i]]=i;
	}
	scanf("%d",&Q);
	for (int i=1;i<=Q;i++)
	{
		scanf("%d%d",&b[i].l,&b[i].r);
		b[i].id=i;
	}
	s.insert(0);
	sort(b+1,b+1+Q,cmp);
	for (int i=1,j=1;i<=Q;i++)
	{
		for (;j<=b[i].r;j++)
			if (pre[j])
			{
				if (!pre[pre[j]]) s.erase(*s.find(pre[j]));
					else seg.update(1,1,n,pre[pre[j]],0);
				seg.update(1,1,n,pre[j],j);
			}
			else s.insert(j);
		int res=max(*(--s.end()),seg.query(1,1,n,1,b[i].l-1));
		if (res>=b[i].l) ans[b[i].id]=a[res];
	}
	for (int i=1;i<=Q;i++) cout<<ans[i]<<"\n";
	return 0;
}
posted @ 2021-06-02 23:07  stoorz  阅读(53)  评论(0编辑  收藏  举报