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;
    }
    
  • 相关阅读:
    Intellij IDEA 打开文件tab数量限制的调整
    Mysql处理中文乱码的问题
    MIT算法导论笔记
    算法导论-排序(一)-插入排序、归并排序
    leetcode题解:Search for a Range (已排序数组范围查找)
    leetcode 题解:Merge Sorted Array(两个已排序数组归并)
    leetcode题解:Construct Binary Tree from Inorder and Postorder Traversal(根据中序和后序遍历构造二叉树)
    leetcode题解:Construct Binary Tree from Preorder and Inorder Traversal (根据前序和中序遍历构造二叉树)
    c++11 std::prev、std::next、std::advance与auto 使用
    (转)指针的引用(*&)与指针的指针(**)
  • 原文地址:https://www.cnblogs.com/quanjun/p/15527327.html
Copyright © 2011-2022 走看看