• 数列找不同


    莫队的一道板子题,可以说是目前看到最简单的可以用莫队的题了。

    题目大意:

    现有数列A1,A2,,AN,Q 个询问(Li,Ri),判断ALi,ALi+1,,ARi 是否互不相同。(支持离线询问)

    首先,我们显然可以看到暴力是一定会超时的,因为暴力的复杂度太大:O(qn)这个直接TLE没商量

    我们说一个稍微慢一些但是也能轻松过这道题的算法:莫队(优雅的暴力)

    莫队总体可以分为四句话:

    1.将整个序列分为若干个块,每一块大小一般都会分为sqrt(n)。

    2.对于整个序列进行排序,以左端点所在块的位置作为第一关键字,以右端点位置作为第二关键字,都做升序排序

    3.暴力求出第一个块的答案

    4.利用已知答案进行转移

    可能大家不太明白第四条,在这里我来解释一下:

    对于这道题来说,a的大小并不大,那么我们就可以开一个tot数组来记录区间有多少个相同的数x,再开一个sum记录有多少不同的数,每一次转移时分为两种情况:

    1.加入一个新元素,我们只要看一下这个数vis数组是否为0,如果为零那么sum++

    2.删去一个旧元素,还是要看是否为0,满足则sum--

    不难发现这些转移都是O(1)的

    那么关于莫队的时间复杂度:

    1.对于右端点来说,由于在同一块内是递增的,每次最多转移n次,而有sqrt(n)个块,所以是O(nsqrt(n))

    2.对于左端点来说,由于每一个块内最多转移n次,而有sqrt(n)个块,所以也是O(nsqrt(n))

    根据加法原理,(这里省略了左端点跨块的复杂度证明,不过也是nsqrt(n)请自行推导)总复杂度为nsqrt(n) (这个是最大复杂度,实际比这个快得多。。。)

    好的,莫队到这里就讲完了!

    最后,附上本题代码:

     1 #include<cstdio>
     2 #include<cmath>
     3 #include<algorithm>
     4 #define maxn 100000
     5 using namespace std;
     6 
     7 int a[maxn+5];
     8 struct query
     9 {
    10     int l,r,id,to;
    11     bool judge;
    12 };
    13 int sum,appear[maxn+5],ans[maxn+5];
    14 query block[maxn+5];
    15 
    16 bool cmp(query x,query y)
    17 {
    18     if(x.id==y.id) return x.r<y.r;
    19     return x.id<y.id;
    20 }
    21 void add(int x)
    22 {
    23     if(appear[a[x]]++==0) sum++;
    24 }
    25 void minus(int x)
    26 {
    27     if(--appear[a[x]]==0) sum--;
    28 }
    29 int main()
    30 {
    31     int n,q;
    32     scanf("%d%d",&n,&q);
    33     for(int i=1; i<=n; i++) scanf("%d",&a[i]);
    34     int siz=sqrt(n);
    35     for(int i=1; i<=q; i++)
    36     {
    37         int x,y;
    38         scanf("%d%d",&x,&y);
    39         block[i].l=x,block[i].r=y;
    40         block[i].id=block[i].l/siz;
    41         block[i].to=i;
    42     }
    43     sort(block+1,block+q+1,cmp);
    44     for(int i=block[1].l; i<=block[1].r; i++)
    45     {
    46         if(appear[a[i]]==0) sum++;
    47         appear[a[i]]++;
    48     }
    49     if(sum==block[1].r-block[1].l+1) ans[block[1].to]=1;
    50     int Li=block[1].l,Ri=block[1].r;
    51     for(int k=2; k<=q; k++)
    52     {
    53         while(block[k].l<Li) add(--Li);
    54         while(block[k].l>Li) minus(Li++);
    55         while(block[k].r>Ri) add(++Ri);
    56         while(block[k].r<Ri) minus(Ri--);
    57         if(sum==block[k].r-block[k].l+1) ans[block[k].to]=1;
    58     }
    59     for(int i=1; i<=q; i++)
    60     {
    61         if(ans[i]==1) printf("Yes
    ");
    62         else printf("No
    ");
    63     }
    64     return 0;
    65 }
  • 相关阅读:
    SQL Server控制语句
    MATLAB中取整函数(fix, floor, ceil, round)的使用
    MATLAB程序设计
    Thinking In Java<<Java编程思想>>
    Boost::bimap
    MySQL学习随笔1
    Boost 1_42_0在windows下的编译及其设置
    MySQL执行mysql脚本及其脚本编写
    Pygame介绍
    Erlang
  • 原文地址:https://www.cnblogs.com/yufenglin/p/10527307.html
Copyright © 2020-2023  润新知