zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    PS 色调——颜色运算
    [ExtJS5学习笔记]第二十二节 Extjs5中使用beforeLabelTpl配置给标签增加必填选项星号标志
    [ExtJS5学习笔记]第二十一节 Extjs5中使用config配置给ext.widget或者create方法传递参数
    PS 滤镜——素描算法(一)
    [ExtJS5学习笔记]第二十节 Extjs5配合数组的push方法,动态创建并加载组件
    [ExtJS5学习笔记]第十九节 Extjs5中通过设置form.Panel的FieldSet集合属性控制多个field集合
    【翻译】Ext JS最新技巧——2014-9-10
    Ext JS 5的声明式事件监听
    BZOJ_3477_[Usaco2014 Mar]Sabotage_二分答案
    BZOJ_3012_[Usaco2012 Dec]First!_trie树+拓扑排序
  • 原文地址:https://www.cnblogs.com/quanjun/p/15527327.html
Copyright © 2011-2022 走看看