• 莫队算法学习


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

     接下来是一道练习题: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 }
    View Code
  • 相关阅读:
    POJ 3261 Milk Patterns (求可重叠的k次最长重复子串)
    UVaLive 5031 Graph and Queries (Treap)
    Uva 11996 Jewel Magic (Splay)
    HYSBZ
    POJ 3580 SuperMemo (Splay 区间更新、翻转、循环右移,插入,删除,查询)
    HDU 1890 Robotic Sort (Splay 区间翻转)
    【转】ACM中java的使用
    HDU 4267 A Simple Problem with Integers (树状数组)
    POJ 1195 Mobile phones (二维树状数组)
    HDU 4417 Super Mario (树状数组/线段树)
  • 原文地址:https://www.cnblogs.com/NineSwords/p/9218820.html
Copyright © 2020-2023  润新知