慢慢的开始重新学习以前的算法了,先从莫队算法学起。ACM反正可以用板子,所以先功利一点,与AC题目无关的细节就不管了,以后有机会再补证明。
先来看板子题:CF86D. Powerful array
题意:给你n个数,m次询问,$K_s$为区间内s的数目,求区间[L,R]之间所有$K_s*K_s*s$的和。($1leq n,mleq 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 ", q[i].ans); 66 } 67 return 0; 68 }
接下来是一道练习题:CF375D. Tree and Queries
题意:给你一棵树n个点,m次询问($nleq 100000,mleq 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}; 65 } 66 sort(q + 1, q + 1 + m, cmp); 67 l = 1, r = 0; 68 for (int i = 1; i <= m; i++) { 69 while (r < q[i].r) { 70 add(a[to[++r]]); 71 } 72 while (r > q[i].r) { 73 del(a[to[r--]]); 74 } 75 while (l < q[i].l) { 76 del(a[to[l++]]); 77 } 78 while (l > q[i].l) { 79 add(a[to[--l]]); 80 } 81 q[i].ans = sum[q[i].k]; 82 } 83 sort(q + 1, q + 1 + m, cmp2); 84 for (int i = 1; i <= m; i++) { 85 printf("%d ", q[i].ans); 86 } 87 return 0; 88 }