SPOJ - DQUERY D-query (莫队入门)

版权声明:本文为蒟蒻原创文章,转载请注明出处哦~ https://blog.csdn.net/a54665sdgf/article/details/82382981

题目链接

题意:给你n个按顺序排列的数a1,a2,...(1 ≤ n ≤ 30000,1 ≤ ai ≤ 1e6),以及q次询问(1 ≤ q ≤ 200000),每个询问都有两个值l,r,让你求出每个询问中[l,r]的区间内不同元素的个数。

解法:很容易看出,假如你已经求出了区间[l,r]内的答案,你就能在O(1)的时间内求出区间[l-1,r],[l+1,r],[l,r-1]或者[l,r+1]内的答案,因此可以考虑用莫队算法。

莫队算法是什么呢?简而言之,就是通过不断移动区间的左右端点l,r值来完成一系列询问的求解。为了使l和r的移动次数尽可能小,我们需要通过对询问进行合理的排序,然后依次求解。怎样排序呢?我们可以把n个数分为sqrt(n)个小区间,每个小区间的元素个数也为sqrt(n)(除了个别区间),然后把区间按照左端点值l所在分块的不同进行排序,对在同一分块内的区间,将它们按照右端点值r的大小进行排序。换言之,就是把区间的左端点值l的所在分块作为第一关键字,右端点值r作为第二关键字进行排序,这样可以使得端点移动得尽可能少,复杂度约为O(sqrt(n)*q),证明也不是很困难,可以自己尝试一下。

顺便推荐两篇我认为比较适合入门的文章:

https://www.cnblogs.com/hzf-sbit/p/4056874.html

https://blog.csdn.net/nhl19961226/article/details/81412134

#define FRER() freopen("i.txt","r",stdin)
#include<bits/stdc++.h>
using namespace std;
const int N=3e4+100;
const int M=1e6+100;
const int maxq=2e5+100;

int a[N],in[N],sz,cnt[M],n,Q;
struct Query
{
    int l,r,i,ans;
    bool operator<(const Query& b)const
    {
        return in[l]!=in[b.l]?in[l]<in[b.l]:r<b.r;
    }
};

Query qr[maxq];
int ans[maxq];

void solve()
{
    sort(qr,qr+Q);
    memset(cnt,0,sizeof cnt);
    memset(ans,0,sizeof ans);
    int L=0,R=-1,now=0;
    for(int i=0; i<Q; ++i)
    {
        int l=qr[i].l,r=qr[i].r;
        while(L<l)
        {
            cnt[a[L++]]--;
            if(cnt[a[L-1]]==0)--now;
        }
        while(L>l)
        {
            cnt[a[--L]]++;
            if(cnt[a[L]]==1)++now;
        }
        while(R<r)
        {
            cnt[a[++R]]++;
            if(cnt[a[R]]==1)++now;
        }
        while(R>r)
        {
            cnt[a[R--]]--;
            if(cnt[a[R+1]]==0)--now;
        }
        ans[qr[i].i]=now;
    }
}

int main()
{
    scanf("%d",&n);
    sz=sqrt(n);
    for(int i=1; i<=n; ++i)
    {
        scanf("%d",&a[i]);
        in[i]=(i-1)/sz;
    }
    scanf("%d",&Q);
    for(int i=0; i<Q; ++i)
    {
        scanf("%d%d",&qr[i].l,&qr[i].r);
        qr[i].i=i;
    }
    solve();
    for(int i=0; i<Q; ++i)
        printf("%d\n",ans[i]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/a54665sdgf/article/details/82382981