CodeForces 1000F One Occurrence( 莫队解法)

F. One Occurrence
time limit per test:3 seconds
memory limit per test:768 megabytes
input:standard input
output:standard output

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?

Input

The first line contains one integer n(1n5105).

The second line contains n integers a1,a2,,an (1ai5105).

The third line contains one integer q(1q5105).

Then q lines follow, i-th line containing two integers li and ri representing i-th query (1lirin).

Output

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.

Example
Input
Copy
6
1 1 2 3 2 4
2
2 6
1 2
Output
Copy
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;
}

猜你喜欢

转载自blog.csdn.net/u013534123/article/details/80948646