莫队算法学习

匿名 (未验证) 提交于 2019-12-03 00:34:01

慢慢的开始重新学习以前的算法了,先从莫队算法学起。ACM反正可以用板子,所以先功利一点,与AC题目无关的细节就不管了,以后有机会再补证明。

先来看板子题:CF86D. Powerful array

题意:给你n个数,m次询问,$K_s$为区间内s的数目,求区间[L,R]之间所有$K_s*K_s*s$的和。($1\leq n,m\leq 200000,a_i \leq 10^6$)

做法:先给序列分个块,按下标每$\sqrt{n}$个数分一块。然后将询问离线后排个序,排序的方法为先按L所在的块的标号从小到大排,对于L所在的块的标号相同的,按照R从小到大排。然后在一一走就好了。

复杂度比较好理解,为$O((m + n)\sqrt{n})$。

 1 #include <cmath>  2 #include <cstdio>  3 #include <cstring>  4 #include <iostream>  5 #include <algorithm>  6 using namespace std;  7 typedef long long ll;  8 const int LEN = 2e5 + 5;  9 ll ans; 10 int i, j, k, n, m, s, t, S, l, r; 11 int a[LEN], pos[LEN], num[1000005]; 12 struct node { 13     int l, r, id; 14     ll ans; 15 } q[LEN]; 16 bool cmp(const node &x, const node &y) { 17     return pos[x.l] < pos[y.l] || (pos[x.l] == pos[y.l] && x.r < y.r); 18 } 19 bool cmp2(const node &x, const node &y) { 20     return x.id < y.id; 21 } 22 ll sqr(ll x) { 23     return x * x; 24 } 25 void add(int x) { 26     ans -= sqr(num[x]) * x; 27     num[x]++; 28     ans += sqr(num[x]) * x; 29 } 30 void del(int x) { 31     ans -= sqr(num[x]) * x; 32     num[x]--; 33     ans += sqr(num[x]) * x; 34 } 35 int main() { 36     scanf("%d %d", &n, &m); 37     S = sqrt(n); 38     for (int i = 1; i <= n; i++) { 39         scanf("%d", &a[i]); 40         pos[i] = (i - 1) / S + 1; 41     } 42     for (int i = 1; i <= m; i++) { 43         scanf("%d %d", &q[i].l, &q[i].r); 44         q[i].id = i; 45     } 46     sort(q + 1, q + 1 + m, cmp); 47     l = 1, r = 0; 48     for (int i = 1; i <= m; i++) { 49         while (r < q[i].r) { 50             add(a[++r]); 51         } 52         while (r > q[i].r) { 53             del(a[r--]); 54         } 55         while (l < q[i].l) { 56             del(a[l++]); 57         } 58         while (l > q[i].l) { 59             add(a[--l]); 60         } 61         q[i].ans = ans; 62     } 63     sort(q + 1, q + 1 + m, cmp2); 64     for (int i = 1; i <= m; i++) { 65         printf("%I64d\n", q[i].ans); 66     } 67     return 0; 68 }
View Code

CF375D. Tree and Queries

题意:给你一棵树n个点,m次询问($n\leq 100000,m\leq 100000$),每个节点有一种颜色, 每次询问问你以v节点为根的子树中,满足同一种颜色的个数$\geq k$的颜色有几个。

ps:这道题莫队并不是最优的做法,还有log的做法,暂时不讨论。

做法:看起来是一道树上的题目,但是还是可以转化成序列上的来做。将树的dfs序搞出来,然后就是序列上[L,R]之间的询问,用数组维护一下颜色的个数几颗。

本题作为CFDiv1的D题算是简单的了,事实上现场A的人也很多。

 1 #include <cmath>  2 #include <cstdio>  3 #include <cstring>  4 #include <iostream>  5 #include <algorithm>  6 using namespace std;  7 const int LEN = 1e5 + 5;  8 int i, j, k, n, m, s, t, ans, tot, Time, S, l, r;  9 struct node { 10     int l, r, k, ans, id; 11 } q[LEN]; 12 struct edge { 13     int vet, next; 14 } E[LEN]; 15 int tid[LEN], a[LEN], size[LEN], num[LEN], sum[LEN], pos[LEN], head[LEN], to[LEN]; 16 void add(int u, int v) { 17     E[++tot] = (edge){v, head[u]}; 18     head[u] = tot; 19 } 20 bool cmp(const node &x, const node &y) { 21     return pos[x.l] < pos[y.l] || (pos[x.l] == pos[y.l] && x.r < y.r); 22 } 23 bool cmp2(const node &x, const node &y) { 24     return x.id < y.id; 25 } 26 void dfs(int u, int pre) { 27     size[u] = 1; 28     tid[u] = ++Time; 29     to[Time] = u; 30     for (int e = head[u]; e != -1; e = E[e].next) { 31         int v = E[e].vet; 32         if (v != pre) { 33             dfs(v, u); 34             size[u] += size[v]; 35         } 36     } 37 } 38 void add(int x) { 39     num[x]++; 40     sum[num[x]]++; 41 } 42 void del(int x) { 43     sum[num[x]]--; 44     num[x]--; 45 } 46 int main() { 47     memset(head, -1, sizeof(head)); 48     scanf("%d %d", &n, &m); 49     S = sqrt(n); 50     for (int i = 1; i <= n; i++) { 51         scanf("%d", &a[i]); 52         pos[i] = (i - 1) / S + 1; 53     } 54     for (int i = 1; i < n; i++) { 55         int x, y; 56         scanf("%d %d", &x, &y); 57         add(x, y); 58         add(y, x); 59     } 60     dfs(1, -1); 61     for (int i = 1; i <= m; i++) { 62         int v, k; 63         scanf("%d %d", &v, &k); 64         q[i] = (node){tid[v], tid[v] + size[v] -1, k, 0, i}; 
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!