慢慢的开始重新学习以前的算法了,先从莫队算法学起。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 }