• [luoguP1972] [SDOI2009]HH的项链(莫队 || 树状数组 || 主席树)


    传送门

    莫队基础题,适合我这种初学者。

    莫队是离线算法,通常不带修改,时间复杂度为 O(n√n)

    我们要先保证通过 [ l , r ] 求得 [ l , r + 1 ] , [ l , r - 1 ] , [ l - 1 , r ] , [ l + 1 , r ] 的效率是O(1)

    对于莫队的理解,移步远航休息栈

    ——代码

     1 #include <cmath>
     2 #include <cstdio>
     3 #include <iostream>
     4 #include <algorithm>
     5 
     6 int n, m, S, ans = 1;
     7 int a[50001], ton[1000001], anslist[200001];
     8 struct node
     9 {
    10     int l, r, id, num;
    11 }q[200001];
    12 
    13 inline int read()
    14 {
    15     int x = 0;
    16     char ch = getchar();
    17     for(; !isdigit(ch); ch = getchar());
    18     for(; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + ch - '0';
    19     return x;
    20 }
    21 
    22 inline bool cmp(node x, node y)
    23 {
    24     return x.id ^ y.id ? x.id < y.id : x.r < y.r;
    25 }
    26 
    27 int main()
    28 {
    29     int i, x, y;
    30     n = read();
    31     for(i = 1; i <= n; i++) a[i] = read();
    32     m = read();
    33     for(i = 1; i <= m; i++) q[i].l = read(), q[i].r = read(), q[i].num = i;
    34     S = sqrt(n);
    35     for(i = 1; i <= m; i++) q[i].id = q[i].l / S + 1;
    36     std::sort(q + 1, q + m + 1, cmp);
    37     x = q[1].l, y = q[1].l;
    38     ton[a[x]]++;
    39     for(i = 1; i <= m; i++)
    40     {
    41         while(x < q[i].l)
    42         {
    43             ton[a[x]]--;
    44             if(!ton[a[x]]) ans--;
    45             x++;
    46         }
    47         while(x > q[i].l)
    48         {
    49             x--;
    50             if(!ton[a[x]]) ans++;
    51             ton[a[x]]++;
    52         }
    53         while(y > q[i].r)
    54         {
    55             ton[a[y]]--;
    56             if(!ton[a[y]]) ans--;
    57             y--;
    58         }
    59         while(y < q[i].r)
    60         {
    61             y++;
    62             if(!ton[a[y]]) ans++;
    63             ton[a[y]]++;
    64         }
    65         anslist[q[i].num] = ans;
    66     }
    67     for(i = 1; i <= m; i++) printf("%d
    ", anslist[i]);
    68     return 0;
    69 }
    View Code

    代码量真是友善啊

    还可以用离线用树状数组来做.

    以下是hzwer的解法:

    这题首先在线是没法做的,所以我们可以考虑离线算法

    首先记录下每种颜色的下一种颜色所在的位置

    将所有询问按照左端点进行排序

    将所有颜色的第一个点x a[x]++

    然后从左往右扫

    扫到一个点x将a[next[x]]++

    碰到一个询问l,r输出sum[r]-sum[l-1]

    其中sum是a数组的前缀和

    求前缀和可以用树状数组

    ——代码

     1 #include <cstdio>
     2 #include <cstring>
     3 #include <iostream>
     4 #include <algorithm>
     5 
     6 const int MAXN = 50001;
     7 int n, m, max;
     8 int head[MAXN], next[MAXN], a[MAXN], ans[MAXN], c[1000001];
     9 struct node
    10 {
    11     int l, r, id;
    12 }q[MAXN];
    13 
    14 inline int read()
    15 {
    16     int x = 0, f = 1;
    17     char ch = getchar();
    18     for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = -1;
    19     for(; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + ch - '0';
    20     return x * f;
    21 }
    22 
    23 inline int Max(int x, int y)
    24 {
    25     return x > y ? x : y;
    26 }
    27 
    28 inline void update(int x)
    29 {
    30     for(; x <= n; x += x & -x) c[x]++;
    31 }
    32 
    33 inline int query(int x)
    34 {
    35     int ret = 0;
    36     for(; x; x -= x & -x) ret += c[x];
    37     return ret;
    38 }
    39 
    40 inline bool cmp(node x, node y)
    41 {
    42     return x.l < y.l;
    43 }
    44 
    45 int main()
    46 {
    47     int i, j;
    48     n = read();
    49     memset(head, -1, sizeof(head));
    50     for(i = 1; i <= n; i++) a[i] = read(), max = Max(max, a[i]);
    51     for(i = n; i; i--) next[i] = head[a[i]], head[a[i]] = i;
    52     for(i = 1; i <= max; i++)
    53         if(head[i] ^ -1)
    54             update(head[i]);
    55     m = read();
    56     for(i = 1; i <= m; i++)
    57     {
    58         q[i].l = read();
    59         q[i].r = read();
    60         q[i].id = i;
    61     }
    62     std::sort(q + 1, q + m + 1, cmp);
    63     j = 1;
    64     for(i = 1; i <= n; i++)
    65     {
    66         while(q[j].l == i) ans[q[j].id] = query(q[j].r) - query(q[j].l - 1), j++;
    67         if(next[i] ^ -1) update(next[i]);
    68     }
    69     for(i = 1; i <= m; i++) printf("%d
    ", ans[i]);
    70     return 0;
    71 }
    View Code

    主席树也是个好方法。

    保存每个点的上一个和它颜色相同的点的位置(如果没有就是0)

    然后以每个点的前驱为下标建主席数。

    统计时只需要统计当前区间 [x,y] 中前驱小于 x 的点的个数

    ——代码

     1 #include <cstdio>
     2 #include <iostream>
     3 
     4 const int MAXN = 50001;
     5 int n, m, cnt;
     6 int a[MAXN], head[1000001], pre[MAXN], root[MAXN], sum[MAXN * 20], ls[MAXN * 20], rs[MAXN * 20];
     7 
     8 inline int read()
     9 {
    10     int x = 0, f = 1;
    11     char ch = getchar();
    12     for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = -1;
    13     for(; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + ch - '0';
    14     return x * f;
    15 }
    16 
    17 inline void update(int &now, int l, int r, int x)
    18 {
    19     ++cnt;
    20     sum[cnt] = sum[now] + 1;
    21     ls[cnt] = ls[now];
    22     rs[cnt] = rs[now];
    23     now = cnt;
    24     if(l == r) return;
    25     int mid = (l + r) >> 1;
    26     if(x <= mid) update(ls[now], l, mid, x);
    27     else update(rs[now], mid + 1, r, x);
    28 }
    29 
    30 inline int query(int x, int y, int l, int r, int k)
    31 {
    32     if(l == r) return sum[y] - sum[x];
    33     int mid = (l + r) >> 1;
    34     if(k <= mid) return query(ls[x], ls[y], l, mid, k);
    35     else return query(rs[x], rs[y], mid + 1, r, k) + sum[ls[y]] - sum[ls[x]];
    36 }
    37 
    38 int main()
    39 {
    40     int i, x, y;
    41     n = read();
    42     for(i = 1; i <= n; i++)
    43     {
    44         a[i] = read();
    45         pre[i] = head[a[i]];
    46         head[a[i]] = i;
    47     }
    48     for(i = 1; i <= n; i++)
    49     {
    50         root[i] = root[i - 1];
    51         update(root[i], 0, n, pre[i]);
    52     }
    53     m = read();
    54     for(i = 1; i <= m; i++)
    55     {
    56         x = read();
    57         y = read();
    58         printf("%d
    ", query(root[x - 1], root[y], 0, n, x - 1));
    59     }
    60     return 0;
    61 }
    View Code
  • 相关阅读:
    雅礼集训2017day5乱写
    任意值域最长公共子序列问题
    雅礼集训2017day4乱写
    雅礼集训2017day2乱写
    SP839
    雅礼集训2017day1乱写
    CF671E
    仅维护当前区间影响类问题的线段树
    「雅礼集训 2017 Day4」编码
    Codeforces Round #503 Div. 2
  • 原文地址:https://www.cnblogs.com/zhenghaotian/p/6880016.html
Copyright © 2020-2023  润新知