这是我的莫队入门题,我也了解到了莫队分为普通莫队以及带修莫队。顾名思义,普通莫队不需要修改区间的值,而带修莫队处理区间的值会修改的查询。
能用莫队的前提条件:
1.在知道 【l, r】中信息时,可以在 O(1)的复杂度内知道 【l - 1, r】,【l + 1, r】,【l, r - 1】,【l, r + 1】的信息,否则复杂度会爆炸。
2.能离线处理。
莫队的时间复杂度为O(n ^ 1.5),但实际效果一定会优于这个时间复杂度。
莫队的主要操作:
1.对查询区间,以 left 来进行分块,然后将分块作为第一关键字来进行从小到大的排序,以right为第二关键字来进行从小到大的排序。
2.找到区间之间的转移方程,边查询边修改。
题目链接:https://www.luogu.org/problemnew/show/P2709
思路:
1.莫队的模板题。
2.值得注意的是扩充区间时,先动指针,再修改值。缩小区间时,先修改值,再动指针。这样做是为了保证区间一定有长度。
代码如下:
1 #include<stdio.h> 2 #include<algorithm> 3 #include<math.h> 4 #include<string.h> 5 #define mem(a, b) memset(a, b, sizeof(a)) 6 using namespace std; 7 8 int n, m, k; 9 int num[50010]; 10 long long cnt[50010]; 11 long long ANS[50010]; 12 13 struct Query 14 { 15 int l, r, id; 16 int pos; 17 }q[50010]; 18 19 bool cmp(Query a, Query b) 20 { 21 if(a.pos != b.pos) 22 return a.pos < b.pos; //第1关键字是块 23 else 24 return a.r < b.r; //第2关键字是右边界 25 } 26 27 int main() 28 { 29 scanf("%d%d%d", &n, &m, &k); 30 mem(cnt, 0); 31 for(int i = 1; i <= n; i ++) 32 scanf("%d", &num[i]); 33 int fk = sqrt(n); 34 for(int i = 1; i <= m; i ++) //莫队要离线处理 35 { 36 scanf("%d%d", &q[i].l, &q[i].r); 37 q[i].id = i; 38 q[i].pos = (q[i].l - 1) / fk + 1; 39 } 40 sort(q + 1, q + 1 + m, cmp); 41 int L = 1, R = 0; 42 long long ans = 0; 43 for(int i = 1; i <= m; i ++) 44 { 45 while(L > q[i].l)//扩充 46 { 47 L --; 48 cnt[num[L]] ++; 49 ans += 2 * cnt[num[L]] - 1; 50 } 51 while(R < q[i].r) //扩充 52 { 53 R ++; 54 cnt[num[R]] ++; 55 ans += 2 * cnt[num[R]] - 1; 56 } 57 while(L < q[i].l)//缩小 58 { 59 cnt[num[L]] --; 60 ans -= 2 * cnt[num[L]] + 1; 61 L ++; 62 } 63 while(R > q[i].r)//缩小 64 { 65 cnt[num[R]] --; 66 ans -= 2 * cnt[num[R]] + 1; 67 R --; 68 } 69 ANS[q[i].id] = ans; 70 } 71 for(int i = 1; i <= m; i ++) 72 { 73 printf("%lld\n", ANS[i]); 74 } 75 return 0; 76 }
原文地址:https://www.cnblogs.com/yuanweidao/p/10986682.html
时间: 2024-10-09 07:20:06