标签:turn clu return 还需 下标 lin 长度 ons contest
题目链接: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;
}
标签:turn clu return 还需 下标 lin 长度 ons contest
原文地址:https://www.cnblogs.com/stoorz/p/14843309.html