【洛谷SP3267】DQUERY - D-query

DQUERY - D-query

题目描述

Given a sequence of \(n\) numbers \(a_{1},a_{2},...,a_{n}\) and a number of d-queries. A d-query is a pair \((i,j)\) \((1 \le i \le j \le n)\). For each d-query \((i,j)\), you have to return the number of distinct elements in the subsequence \(a_{i},a _{i+1},...,a_{j}\).

输入格式

  • Line 1: \(n\) \((1 \le n le 30000)\).
  • Line 2: n numbers \(a_{1},a_{2},...,a_{n}\) \((1 \le a_{i} \le 10^{6})\).
  • Line 3: \(q\) \((1 \le q \le 200000)\), the number of d-queries.
  • In the next \(q\) lines, each line contains \(2\) numbers \(i\),\(j\) representing a d-query \((1 \le i \le j \le n)\).

输出格式

  • For each d-query \((i,j)\), print the number of distinct elements in the subsequence \(a_{i},a_{i+1},...,a_{j}\) in a single line.

题意翻译

给出一个长度为\(n\)的数列,\(a_{1},a_{2},...,a_{n}\),有\(q\)个询问,每个询问给出数对\((i,j)\)\((i,j)\),需要你给出\(a_{i},a_{i+1},...,a_{j}\)这一段中有多少不同的数字

样例输入

5
1 1 2 1 3
3
1 5
2 4
3 5

样例输出

3
2
3

题解

题意:看翻译吧。。。
这是一道莫队板题。(明显的)
本人其实也是刚学莫队,对于莫队可能也有一些误解,如果有不正确的解释请各位大佬指出。
因为做到一个据说需要莫队的题目,所以开始学习莫队了。
原本以为莫队是很难的,看了别人的博客后感觉看不懂(优雅的暴力?),然后开始看别人写的代码和板题。
然后。。。
这真TM是优雅的暴力!!!
好了,吐槽结束,其实大家只要知道莫队其实本质是暴力,只不过用特殊的方法排了个序而已。
我们考虑这样一种暴力,用\(cnt[]\)\(l,r\)维护\(l\)\(r\)区间内各个数字的个数(因为数字的大小最大为\(10^6\)),并且维护一个\(ans\)值,然后我们移动\(l\)\(r\)的时候可以快速地维护这个数组和\(ans\)值了。
但是我们可以发现直接这样维护的话时间复杂度还是\(O(n^2)\)的。
那么我们考虑离线操作,对输入的询问排个序。
如果我们按照询问的\(l\)值从小到大排序,使维护的区间的\(l\)只会往右移动,那么可能可以减少一点时间,但是想一下就会发现这种方法是可能被卡成\(O(n^2)\)的,比如询问按照\(l\)排序后\(r\)的值是一大一小,那么每次询问的时间复杂度都是\(O(n)\)
那么我们开始讲莫队,莫队就是利用了分块的思想,我们把询问中\(\frac {l}{\sqrt{n}}\)相同的数分为一组(这里分块的大小是\(\sqrt{n}\),一般来说随机数据的话区\(\sqrt{n}\)会比较好,但是不同的题目分块的大小也可以不同)
然后在同一组内的询问,我们按照询问的\(r\)值从小到大排序。
这样你就会发现在每\(\sqrt{n}\)次询问内,因为每个区间\(r\)值是从小到大排序的,所以你区间移动的距离最多为\(n+\sqrt{n}*\sqrt{n}\)(近似为\(n\)),那么莫队的时间复杂度就是\(O(n\sqrt{n})\)
上代码:

#include<bits/stdc++.h>
using namespace std;
int n,m;
int a[30009];
struct aa{
    int l,r,x,ans;
}p[200009];
int sq;
bool cmp(aa x,aa y){//按照莫队算法排序
    if(x.l/sq<y.l/sq) return 1;
    if(x.l/sq>y.l/sq) return 0;
    return x.r<y.r;
}
int cnt[1000009];
bool cmpp(aa x,aa y){return x.x<y.x;}
int main(){
    scanf("%d",&n);
    sq=sqrt(n);
    for(int j=1;j<=n;j++)
        scanf("%d",&a[j]);
    scanf("%d",&m);
    for(int j=1;j<=m;j++){
        scanf("%d%d",&p[j].l,&p[j].r);
        p[j].x=j;//因为最后输出要按输入的顺序输出,所以这里记录输入的顺序
    }
    sort(p+1,p+m+1,cmp);
    int ll=1,rr=1,ss=1;
    cnt[a[1]]=1;
    for(int j=1;j<=m;j++){
        while(rr<p[j].r){
            rr++;
            cnt[a[rr]]++;
            if(cnt[a[rr]]==1) ss++;
        }
        while(ll>p[j].l){
            ll--;
            cnt[a[ll]]++;
            if(cnt[a[ll]]==1) ss++;
        }
        while(rr>p[j].r){
            cnt[a[rr]]--;
            if(cnt[a[rr]]==0) ss--;
            rr--;
        }
        while(ll<p[j].l){
            cnt[a[ll]]--;
            if(cnt[a[ll]]==0) ss--;
            ll++;
        }
        p[j].ans=ss;//记录答案
    }
    sort(p+1,p+m+1,cmpp);
    for(int j=1;j<=m;j++)
        printf("%d\n",p[j].ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/linjiale/p/11761445.html