You are given an array a consisting of n integers, and q queries to it. i-th query is denoted by two integers li and ri. For each query, you have to find any integer that occurs exactly once in the subarray of a from index li to index ri (a subarray is a contiguous subsegment of an array). For example, if a=[1,1,2,3,2,4], then for query (li=2,ri=6) the subarray we are interested in is [1,2,3,2,4], and possible answers are 1, 3 and 4; for query (li=1,ri=2) the subarray we are interested in is [1,1], and there is no such element that occurs exactly once.
Can you answer all of the queries?
The first line contains one integer n(1≤n≤5⋅105).
The second line contains n integers a1,a2,…,an (1≤ai≤5⋅105).
The third line contains one integer q(1≤q≤5⋅105).
Then q lines follow, i-th line containing two integers li and ri representing i-th query (1≤li≤ri≤n).
Answer the queries as follows:
If there is no integer such that it occurs in the subarray from index li to index ri exactly once, print 0. Otherwise print any such integer.
6 1 1 2 3 2 4 2 2 6 1 2
4 0
题意就不再次陈述了……
其实这题首先想到的还是用莫队伍去水过,但是这一水到也是学到了东西。首先说一说莫队的朴素做法。用一个set记录当前包含区间的出现次数为一次的数字,然后每次改变区间的时候维护每个数字出现的次数t[i]还有集合set,可以用unordered_set加速。但是即便如此还是TLE,而且不是一点。
分析原因。首先,用set的好处就是最后求解答案的时候很方便,只需要输出set里面随便一个数字即可,但区间变动频繁,即便使用了unordered_set,时间也会在频繁的insert和erase操作中被大量浪费,因为复杂度还是比O(1)大的。那么,我们为什么不选择把时间花在次数相对较少的求解答案,而用O(1)的方法实现区间的变动呢。于是,我们继续利用分块的思想,对于每一个块i,T[i]表示这个块中的数字出现一次的个数。求解答案的时候,首先遍历块,找到含有出现一次数字的块,然后再到块中间去确定这个数字,这样一次求解的复杂度是O(N^0.5),但是区间端点移动的复杂度变成了O(1)。这样求解复杂度和端点移动复杂度都是O(QN^0.5),50W的数据理论上可以在3s。
但是,实际上还是会TLE。这里,之前没有注意,朴素的莫队排序是先按照block排序再按照r排序,但更快的一种方式是,首先按照block排序,再分奇偶,奇数按照r升序排序,偶数按照r降序排序。这样可以保证区间端点的连续性更好,实际的复杂度也更低,但总的来说还是比线段树解法慢很多的。具体见代码:
#include<bits/stdc++.h>
#define N 500010
using namespace std;
int n,m,q,blocks,a[N],t[N],ans[N],T[800],tot;
struct query{int l,r,block,id;} Q[N];
bool cmp(query a,query b)
{
if (a.block!=b.block) return a.l<b.l;
if (a.block&1) return a.r<b.r; return a.r>b.r;
}
int cal()
{
if (tot==0) return 0;
for(int i=0;i<blocks;i++)
{
if(T[i])
for(int j=i*blocks;;j++)
if (t[j]==1) return j;
}
}
int main()
{
scanf("%d",&n);
blocks=sqrt(n)+1;
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
scanf("%d",&q);
for(int i=1;i<=q;i++)
{
scanf("%d%d",&Q[i].l,&Q[i].r);
Q[i].id=i; Q[i].block=Q[i].l/blocks;
}
int l=1,r=0;
sort(Q+1,Q+1+q,cmp);
for(int i=1;i<=q;i++)
{
while(l<Q[i].l)
{
if (--t[a[l]]==0) --T[a[l]/blocks],--tot;
else if (t[a[l]]==1) ++T[a[l]/blocks],++tot;
l++;
}
while(l>Q[i].l)
{
l--;
if (++t[a[l]]==2) --T[a[l]/blocks],--tot;
else if (t[a[l]]==1) ++T[a[l]/blocks],++tot;
}
while(r<Q[i].r)
{
r++;
if (++t[a[r]]==2) --T[a[r]/blocks],--tot;
else if (t[a[r]]==1) ++T[a[r]/blocks],++tot;
}
while(r>Q[i].r)
{
if (--t[a[r]]==0) --T[a[r]/blocks],--tot;
else if (t[a[r]]==1) ++T[a[r]/blocks],++tot;
r--;
}
ans[Q[i].id]=cal();
}
for(int i=1;i<=q;i++)
printf("%d\n",ans[i]);
return 0;
}