• 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;
    }
    
  • 相关阅读:
    html单引号,双引号转义
    把文章里边的html标签去掉(去掉文字的样式,显示css设置的样式)
    java缓存适合使用的情况
    Java内存缓存
    springmvc怎么重定向,从一个controller跳到另一个controller
    jquery 获取标签名(tagName)
    jQuery判断checkbox是否选中的3种方法
    判断一组checkbox中是否有被选中的
    在O(N)时间内求解 正数数组中 两个数相加的 最大值
    两种方法求解 正数数组中 两个数相减 的最大值
  • 原文地址:https://www.cnblogs.com/quanjun/p/15527327.html
Copyright © 2020-2023  润新知