数列找不同

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

题目大意:

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

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

那么我们考虑如何进行改良,如果我们利用线段树,每一个节点记录l和r,并且还要记录该区间的不同的数。

这个确实快的一批,O(qlogn)

但是我们今天并不想说它,虽然线段树很快,但是这个代码量实在令人恐慌。。。

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

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

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\n");
62         else printf("No\n");
63     }
64     return 0;
65 }

猜你喜欢

转载自www.cnblogs.com/yufenglin/p/10527307.html