LOJ6284. 数列分块入门 8 题解

题目链接:https://loj.ac/p/6284

涉及操作:

  1. 区间查询某一个数 (c) 出现的次数;
  2. 区间更新。

解题思路:

一开始的思路是除了整块维护以外,再对每一个区间用一个 multiset 维护每一个数出现的次数。这样更新和查询一次的时间复杂度都会降到 (O(sqrt n log n)),总的时间复杂度是 (O(n cdot sqrt n log n)),但是超时了,只有30分。

30分代码:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 100010;
int n, blo, a[maxn], bl[maxn], tag[505]; // tag[i] 表示第i个分块在被完全覆盖时对应的那个数
bool flag[505]; // flag[i] 表示第i个分块是否全被一个数覆盖
unordered_multiset<int> st[505];

void recover(int p) {
    if (flag[p]) {
        flag[p] = false;
        st[p].clear();
        for (int i = (p-1)*blo+1; i <= min(p*blo, n); i ++) {
            a[i] = tag[p];
            st[p].insert(a[i]);
        }
    }
}

void update(int l, int r, int c) {
    recover(bl[l]);
    for (int i = l; i <= min(bl[l]*blo, r); i ++) {
        st[bl[l]].erase(st[bl[l]].find(a[i]));
        a[i] = c;
        st[bl[l]].insert(c);
    }
    if (bl[l] != bl[r]) {
        recover(bl[r]);
        for (int i = (bl[r]-1)*blo+1; i <= r; i ++) {
            st[bl[r]].erase(st[bl[r]].find(a[i]));
            a[i] = c;
            st[bl[r]].insert(c);
        }
    }
    for (int i = bl[l]+1; i < bl[r]; i ++) {
        flag[i] = true;
        tag[i] = c;
    }
}

int query(int l, int r, int c) {
    int ans = 0;
    recover(bl[l]);
    for (int i = l; i <= min(bl[l]*blo, r); i ++)
        if (a[i] == c)
            ans ++;
    if (bl[l] != bl[r]) {
        recover(bl[r]);
        for (int i = (bl[r]-1)*blo+1; i <= r; i ++)
            if (a[i] == c)
                ans ++;
    }
    for (int i = bl[l]+1; i < bl[r]; i ++) {
        if (flag[i] && tag[i] == c)
            ans += blo;
        else if (!flag[i])
            ans += st[i].count(c);
    }
    return ans;
}

int main() {
    ios::sync_with_stdio(0);
    cin >> n;
    blo = sqrt(n);
    for (int i = 1; i <= n; i ++) {
        cin >> a[i];
        bl[i] = (i - 1) / blo + 1;
        st[bl[i]].insert(a[i]);
    }
    for (int i = 0; i < n; i ++) {
        int l, r, c;
        cin >> l >> r >> c;
        cout << query(l, r, c) << endl;
        update(l, r, c);
    }
    return 0;
}

然后改成用 map 试一试 手动狗头

(其实写上面这句话的时候我没感觉 map 能过,然后我就想要狗头试一下 map,所以我估计是 multiset 常数比较大吧;另一方面我也觉得原作者的那个写法时间复杂度是有点高的 https://loj.ac/d/3445

然后过了…… (可能 multiset 比较耗时吧)

100分程序:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 100010;
int n, blo, a[maxn], bl[maxn], tag[505]; // tag[i] 表示第i个分块在被完全覆盖时对应的那个数
bool flag[505]; // flag[i] 表示第i个分块是否全被一个数覆盖
// unordered_multiset<int> st[505];
map<int, int> mp[505];

void recover(int p) {
    if (flag[p]) {
        flag[p] = false;
        mp[p].clear();
        for (int i = (p-1)*blo+1; i <= min(p*blo, n); i ++) {
            a[i] = tag[p];
            mp[p][a[i]] ++;
        }
    }
}

void update(int l, int r, int c) {
    recover(bl[l]);
    for (int i = l; i <= min(bl[l]*blo, r); i ++) {
        mp[bl[l]][a[i]] --;
        a[i] = c;
        mp[bl[l]][c] ++;
    }
    if (bl[l] != bl[r]) {
        recover(bl[r]);
        for (int i = (bl[r]-1)*blo+1; i <= r; i ++) {
            mp[bl[r]][a[i]] --;
            a[i] = c;
            mp[bl[r]][a[i]] ++;
        }
    }
    for (int i = bl[l]+1; i < bl[r]; i ++) {
        flag[i] = true;
        tag[i] = c;
    }
}

int query(int l, int r, int c) {
    int ans = 0;
    recover(bl[l]);
    for (int i = l; i <= min(bl[l]*blo, r); i ++)
        if (a[i] == c)
            ans ++;
    if (bl[l] != bl[r]) {
        recover(bl[r]);
        for (int i = (bl[r]-1)*blo+1; i <= r; i ++)
            if (a[i] == c)
                ans ++;
    }
    for (int i = bl[l]+1; i < bl[r]; i ++) {
        if (flag[i] && tag[i] == c)
            ans += blo;
        else if (!flag[i])
            ans += mp[i][c];
    }
    return ans;
}

int main() {
    ios::sync_with_stdio(0);
    cin >> n;
    blo = sqrt(n);
    for (int i = 1; i <= n; i ++) {
        cin >> a[i];
        bl[i] = (i - 1) / blo + 1;
        mp[bl[i]][a[i]] ++;
    }
    for (int i = 0; i < n; i ++) {
        int l, r, c;
        cin >> l >> r >> c;
        cout << query(l, r, c) << endl;
        update(l, r, c);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/quanjun/p/15527327.html