莫队算法——普通莫队
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=lrai∗num(ai)
num(ai)num(a_i)num(ai)代表aia_iai在这个区间中出现的次数。
你能帮帮他吗?
给你n个数,每个数为aia_iai,现在有m个询问,每个询问l,r,需要求出:
∑i=lrai∗num(ai)\\sum_{i=l}^r a_i*num(a_i)∑i=lrai∗num(ai)
num(ai)num(a_i)num(ai)代表aia_iai在这个区间中出现的次数。
你能帮帮他吗?
输入描述:
第一行,两个整数n,m
第二行,总共n个数,代表这个数列
接下来m行,每行两个整数l,r,代表一个询问
输出描述:
输出总共m行,对于每个询问,输出这个询问对应的答案
备注:
1≤n,m≤1e51\\leq n,m\\leq 1e51≤n,m≤1e5
1≤ai≤1e51\\leq a_i\\leq 1e51≤ai≤1e5
1≤l,r≤n1\\leq l,r\\leq n1≤l,r≤n
以下附有注释(我都注释成这个样子了,应该都是看得懂的吧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 }
摸鱼好久了,终于脑子里有东西能更新了(咸鱼)
以上是关于莫队算法——普通莫队的主要内容,如果未能解决你的问题,请参考以下文章