莫队算法——普通莫队

Posted 20kmのshimakaze

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了莫队算法——普通莫队相关的知识,希望对你有一定的参考价值。

以下介绍莫队算法及我个人见解:

莫队算法就是一种暴力,但这种暴力效率很高,核心思想就是分块,维护左右指针将答案一个个算出来。特点是要求数据离线。

其核心是将要求的区间按一定方法排序,以达到前一个区间到此区间维护左右指针的次数能尽可能少。

至于维护答案则要看具体情况进行修改。

以下是牛客的区间求和问题。

链接:https://ac.nowcoder.com/acm/contest/16910/L
来源:牛客网

题目描述

小sun最近突然对区间来了兴趣,现在他有这样一个问题想问问你:
给你n个数,每个数为aia_iai,现在有m个询问,每个询问l,r,需要求出:
∑i=lrai∗num(ai)\\sum_{i=l}^r a_i*num(a_i)i=lrainum(ai)

num(ai)num(a_i)num(ai)代表aia_iai在这个区间中出现的次数。
你能帮帮他吗?

输入描述:

第一行,两个整数n,m

第二行,总共n个数,代表这个数列

接下来m行,每行两个整数l,r,代表一个询问

输出描述:

输出总共m行,对于每个询问,输出这个询问对应的答案
示例1

输入

复制
10 5
1 3 2 4 5 6 4 5 6 7
1 5
2 5
3 4
1 10
3 7

输出

复制
15
14
6
73
29

备注:

1≤n,m≤1e51\\leq n,m\\leq 1e51n,m1e5
1≤ai≤1e51\\leq a_i\\leq 1e51ai1e5
1≤l,r≤n1\\leq l,r\\leq n1l,rn

以下附有注释(我都注释成这个样子了,应该都是看得懂的吧2333=v=)
 1 #include<iostream>
 2 #include<algorithm>
 3 #include<cmath>
 4 using namespace std;
 5 #define llo long long
 6 llo n, m, sqn, ans, a[100007], w[100007], sum[100007];//a原数组,w出现次数,id位置sum答案
 7 struct point
 8 {
 9     llo l, r, id;    //l左指针,r右指针,id表示数据输入顺序
10 }s[100007];
11 bool cmp(point a, point b)    //sort排序规则
12 {
13     if (a.l / sqn != b.l / sqn)    //左指针在同一分块时,按右指针从小到大排序
14         return a.l < b.l;
15     return a.r < b.r;            //否则,左指针不在同一分块时,按左指针从小到大排序
16 }
17 void updata(llo pos, llo i)//pos指针位置,i为加减状态
18 {
19     llo v = a[pos];            //v为数组数值
20     ans -= v * w[v] * w[v];    //将此区间所有关于v的数值去除
21     w[v] += i;                //将在区间出现次数修改
22     ans += v * w[v] * w[v];    //将修改后此区间所有关于v的数值更改
23 }
24 int main()
25 {
26     cin >> n >> m;
27     sqn = (int)sqrt(n);                //分块,一块大小为根号n个数据
28     for (llo i = 1;i <= n;i++)        //输入
29         scanf("%lld", &a[i]);
30     for (llo i = 1;i <= m;i++) {
31         scanf("%lld%lld", &s[i].l, &s[i].r);
32         s[i].id = i;
33     }
34     sort(s + 1, s + 1 + m, cmp);//排序
35     llo ll = 0, rr = 0;            //左右指针先放到最左边
36     for (llo i = 1;i <= m;i++) {//m个区间
37         while (rr < s[i].r) {    //右指针在区间左边
38             updata(++rr, 1);    //需要指针自增再去修改数据(++rr和rr++在此需要特别留心)
39         }
40         while (rr > s[i].r) {    //总体规律为区间增大就先改变自己再修改数据
41             updata(rr--, -1);
42         }
43         while (ll < s[i].l) {    //将指针挪动至区间两端
44             updata(ll++, -1);
45         }
46         while (ll > s[i].l) {
47 
48             updata(--ll, 1);
49         }
50         sum[s[i].id] = ans;        //将答案存入sum数组
51     }
52     for (int i = 1;i <= m;i++)//按照先后顺序输出答案
53         printf("%lld\\n", sum[i]);
54     return 0;
55 }

  摸鱼好久了,终于脑子里有东西能更新了(咸鱼)

以上是关于莫队算法——普通莫队的主要内容,如果未能解决你的问题,请参考以下文章

莫队小结

浅谈普通莫队算法

莫队

莫队算法

莫队算法&#183;初探总结

莫队入门