zoukankan      html  css  js  c++  java
  • loj 数列分块入门 6 9(区间众数)

    6

    题意

    给出一个长为(n)的数列,以及(n)个操作,操作涉及单点插入,单点询问,数据随机生成。

    题解

    参考:http://hzwer.com/8053.html

    每个块内用一个(vector)维护,每次插入时先找到位置所在的块,再暴力插入。

    如果数据不随机,即如果先在一个块有大量单点插入,这个块的大小会大大超过(sqrt n),那块内的暴力就没有复杂度保证了。

    为此引入一个操作:重新分块(重构)

    (sqrt n)次插入后,重新把数列平均分一下块,重构需要的复杂度为(O(n)),重构的次数为(sqrt n),所以重构的复杂度没有问题,而且保证了每个块的大小相对均衡。

    当然,也可以当某个块过大时重构,或者只把这个块分成两半。

    // 代码中采取的是当块过大时进行重构。

    Code

    #include <bits/stdc++.h>
    #define maxn 200010
    #define C 20
    #define F(i, a, b) for (int i = (a); i < (b); ++i)
    #define F2(i, a, b) for (int i = (a); i <= (b); ++i)
    #define dF(i, a, b) for (int i = (a); i > (b); --i)
    #define dF2(i, a, b) for (int i = (a); i >= (b); --i)
    using namespace std;
    typedef long long LL;
    int n, blo, a[maxn], num;
    vector<int> v[510];
    struct node { int bl, p; };
    node query(int p) {
        int i=0;
        while (p>v[i].size()) p -= v[i++].size();
        return {i, p-1};
    }
    void rebuild() {
        int cnt=0;
        F(i, 0, num) {
            for (auto x : v[i]) a[cnt++] = x;
            v[i].clear();
        }
        blo = sqrt(cnt); num = (cnt+blo-1)/blo;
        F(i, 0, cnt) v[i/blo].push_back(a[i]);
    }
    void insert(int p, int x) {
        node nd = query(p);
        v[nd.bl].insert(v[nd.bl].begin()+nd.p, x);
        if (v[nd.bl].size()>C*blo) rebuild();
    }
    int main() {
        scanf("%d", &n); blo = sqrt(n);
        F(i, 0, n) {
            scanf("%d", &a[i]);
            v[i/blo].push_back(a[i]);
        }
        num = (n+blo-1)/blo;
        F(i, 0, n) {
            int op, l, r, c;
            scanf("%d%d%d%d", &op, &l, &r, &c);
            if (op) {
                node nd = query(r);
                printf("%d
    ", v[nd.bl][nd.p]);
            }
            else insert(l, r);
        }
        return 0;
    }
    

    9

    题意

    给出一个长为(n)的数列,以及(n)个操作,操作涉及询问区间的最小众数。

    题解

    参考:http://hzwer.com/3582.html

    1. 离散化

    2. 对每一个数开一个,即对每个数用一个(vector)按序记录它所有的出现位置。

    3. 预处理(f[s][t])表示第(s)块到第(t)块的最小众数:
      方法是:枚举(s),向右扫,每扫一个块得到一个值。
      复杂度:(sqrt n*sqrt n+(sqrt n-1)*sqrt n+cdots+2*sqrt n+sqrt n=sqrt n*(1+2+cdots+sqrt n)=O(nsqrt n))

    4. 对于一个询问([l,r])
      不完整的块中共有(2sqrt n)个数,完整的块根据3. 中的预处理得到一个数。
      暴力比较这(2sqrt n+1)个数的出现次数,
      方法是:要知道(x)([l,r])中的出现次数,只需在(vector[x])中进行二分查找。
      复杂度:(O(sqrt n*logn))

    Code

    #include <bits/stdc++.h>
    #define F(i, a, b) for (int i = (a); i < (b); ++i)
    #define F2(i, a, b) for (int i = (a); i <= (b); ++i)
    #define dF(i, a, b) for (int i = (a); i > (b); --i)
    #define dF2(i, a, b) for (int i = (a); i >= (b); --i)
    #define maxn 100010
    #define inf 0x3f3f3f3f
    using namespace std;
    int cnt[maxn], a[maxn], n, num, blo, f[1010][1010], bl[maxn], mp2[maxn];
    map<int, int> mp;
    vector<int> v[maxn];
    typedef long long LL;
    inline void update(int temp, int& maxx, int x, int& ans) {
        if (temp>maxx || (temp==maxx&&mp2[x]<mp2[ans])) {
            maxx = temp, ans = x;
        }
    }
    void pre(int s) {
        memset(cnt, 0, sizeof cnt);
        int ans = -inf, maxx = 0;
        F(i, s, num) {
            F(j, i*blo, min((i+1)*blo, n)) {
                ++cnt[a[j]];
                update(cnt[a[j]], maxx, a[j], ans);
            }
            f[s][i] = ans;
        }
    }
    inline int count(int l, int r, int x) {
        return upper_bound(v[x].begin(), v[x].end(), r)-upper_bound(v[x].begin(), v[x].end(), l-1);
    }
    
    int query(int l, int r) {
        int ans = -inf, maxx = 0;
        F(i, l, min(r+1, (bl[l]+1)*blo)) {
            int temp = count(l, r, a[i]);
            update(temp, maxx, a[i], ans);
        }
        if (bl[l]!=bl[r]) {
            F2(i, bl[r]*blo, r) {
                int temp = count(l, r, a[i]);
                update(temp, maxx, a[i], ans);
            }
        }
        if (bl[l]+1<=bl[r]-1) {
            int x = f[bl[l]+1][bl[r]-1];
            int temp = count(l, r, x);
            update(temp, maxx, x, ans);
        }
        return ans;
    }
    int main() {
        scanf("%d", &n); blo = sqrt(n);
        int tot=0;
        F(i, 0, n) {
            scanf("%d", &a[i]);
            if (!mp[a[i]]) {
                mp[a[i]] = ++tot;
                mp2[tot] = a[i];
            }
            bl[i] = i/blo;
            v[a[i]=mp[a[i]]].push_back(i);
        }
        num = bl[n-1]+1;
        F(i, 0, num) pre(i);
        F(i, 0, n) {
            int l, r;
            scanf("%d%d", &l, &r); --l, --r;
            printf("%d
    ", mp2[query(l, r)]);
        }
        return 0;
    }
    
    
  • 相关阅读:
    APUE.3源码编译(Ubuntu16.04)
    《UNIX环境高级编程》(第三版)阅读笔记---2018-5-9
    css回归之用户界面
    css回归之文本
    js回归之字符串
    js回归之BOM
    js回归之事件
    百度前端面试总结
    书单
    剑指offer做题收获之一:补码
  • 原文地址:https://www.cnblogs.com/kkkkahlua/p/8479216.html
Copyright © 2011-2022 走看看