Siyuan

「BZOJ 2120」数颜色
「BZOJ 2120」数颜色
扫描右侧二维码阅读全文
15
2019/02

「BZOJ 2120」数颜色

<!--index-menu-->

Description

题目链接:BZOJ 2120

墨墨购买了一套 $n​$ 支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问。墨墨会向你发布如下指令:

  • Q l r:询问从第 $l$ 支画笔到第 $r$ 支画笔中共有几种不同颜色的画笔。
  • R p c :把第 $p$ 支画笔替换为颜色 $c$。

数据范围:$1\le n,m\le 5\times 10^4$,$1\le c\le 10^6​$。


Solution

由于没有区间可加性,我们考虑带修改莫队。根据「算法笔记」带修改莫队算法 中的分析,我们直接 $n^{\frac{2}{3}}$ 分块,然后把询问离线下来,按照套路直接做就行了(Orz 某位叫做 $\color{black}{\text{Q}}\color{red}{\text{Y}}$ 的神仙说可以树套树)。

据说这题分块 + $\text{bitset}​$ 可以卡过去?但是我卡了一个晚上常数还是只有 $80​$ 分啊 QAQ

时间复杂度:$\mathcal O(n^\frac{5}{3})$。


Code

分块

#include <cstdio>
#include <cmath>
#include <algorithm>
#include <bitset>

const int N = 1e5 + 5, M = 230;

int n, m, len, num, a[N], v[N], p[N][3], bl[N], l[M], r[M], cnt[M][N];
std::bitset<N> f[M];

void read(int &t) {
    t = 0;
    char c = getchar();
    for (; c < '0' || c > '9'; c = getchar());
    for (; c >= '0' && c <= '9'; t = t * 10 + (c ^ 48), c = getchar());
}
void print(int x) {
    if (x > 9) print(x / 10);
    putchar(x % 10 + '0');
}
char getc() {
    char c = getchar();
    while (c != 'Q' && c != 'R') c = getchar();
    return c;
}
void build() {
    len = sqrt(n), num = (n - 1) / len + 1;
    for (int i = 1; i <= n; i++) {
        bl[i] = (i - 1) / len + 1;
        if (++cnt[bl[i]][a[i]] == 1) f[bl[i]][a[i]] = 1;
    }
    for (int i = 1; i <= num; i++) {
        l[i] = (i - 1) * len + 1, r[i] = i * len;
    }
    r[num] = n;
}
void modify(int x, int c) {
    if (--cnt[bl[x]][a[x]] == 0) f[bl[x]][a[x]] = 0;
    a[x] = c;
    if (++cnt[bl[x]][a[x]] == 1) f[bl[x]][a[x]] = 1;
}
int query(int x, int y) {
    std::bitset<N> ans;
    if (bl[x] == bl[y]) {
        for (int i = x; i <= y; i++) ans[a[i]] = 1;
    } else {
        for (int i = bl[x] + 1; i <= bl[y] - 1; i++) ans |= f[i];
        for (int i = x; i <= r[bl[x]]; i++) ans[a[i]] = 1;
        for (int i = l[bl[y]]; i <= y; i++) ans[a[i]] = 1;
    }
    return ans.count();
}
int main() {
    read(n), read(m);
    int tot = 0;
    for (int i = 1; i <= n; i++) {
        read(a[i]), v[++tot] = a[i];
    }
    for (int i = 1; i <= m; i++) {
        p[i][0] = (getc() == 'R');
        read(p[i][1]), read(p[i][2]);
        if (p[i][0]) v[++tot] = p[i][2];
    }
    std::sort(v + 1, v + tot + 1);
    tot = std::unique(v + 1, v + tot + 1) - (v + 1);
    for (int i = 1; i <= n; i++) {
        a[i] = std::lower_bound(v + 1, v + tot + 1, a[i]) - v;
    }
    for (int i = 1; i <= m; i++) {
        if (p[i][0]) p[i][2] = std::lower_bound(v + 1, v + tot + 1, p[i][2]) - v;
    }
    build();
    for (int i = 1; i <= m; i++) {
        if (p[i][0]) {
            modify(p[i][1], p[i][2]);
        } else {
            print(query(p[i][1], p[i][2])), puts("");
        }
    }
    return 0;
}

莫队

#include <cstdio>
#include <cmath>
#include <algorithm>

const int N = 5e4 + 5, M = 1e6 + 5;

int n, m, cnt1, cnt2, ret, a[N], b[N], bl[N], cnt[M], ans[N];

struct Data1 {
    int l, r, t, id;
    Data1(int _l = 0, int _r = 0, int _t = 0, int _id = 0) {
        l = _l, r = _r, t = _t, id = _id;
    }
    bool operator<(const Data1 &b) const {
        return bl[l] == bl[b.l] ? bl[r] == bl[b.r] ? t < b.t : r < b.r : l < b.l;
    }
} q[N];

struct Data2 {
    int x, u, v;
    Data2(int _x = 0, int _u = 0, int _v = 0) {
        x = _x, u = _u, v = _v;
    }
} r[N];

void add(int c) {
    if (++cnt[c] == 1) ret++;
}
void del(int c) {
    if (--cnt[c] == 0) ret--;
}
void modify(int l, int r, int x, int c) {
    if (l <= x && x <= r) del(a[x]), add(c);
    a[x] = c;
}
int main() {
    scanf("%d%d", &n, &m);
    int len = pow(n, 2.0 / 3);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i]), b[i] = a[i];
        bl[i] = (i - 1) / len + 1;
    }
    for (int i = 1; i <= m; i++) {
        char s[5];
        int x, y;
        scanf("%s%d%d", s + 1, &x, &y);
        if (s[1] == 'Q') {
            cnt1++, q[cnt1] = Data1(x, y, cnt2, cnt1);
        } else {
            cnt2++, r[cnt2] = Data2(x, b[x], y), b[x] = y;
        }
    }
    std::sort(q + 1, q + cnt1 + 1);
    for (int L = 1, R = 0, T = 0, i = 1; i <= cnt1; i++) {
        int l = q[i].l, r = q[i].r, t = q[i].t;
        while (T < t) T++, modify(L, R, r[T].x, r[T].v);
        while (T > t) modify(L, R, r[T].x, r[T].u), T--;
        while (L < l) del(a[L++]);
        while (L > l) add(a[--L]);
        while (R < r) add(a[++R]);
        while (R > r) del(a[R--]);
        ans[q[i].id] = ret;
    }
    for (int i = 1; i <= cnt1; i++) {
        printf("%d\n", ans[i]);
    }
    return 0;
}
最后修改:2019 年 05 月 19 日 10 : 59 AM

发表评论