• 分块------定期重构


    定期重构

    讲真的,定期重构这东西在网上博客蛮少的

    hzwer出了数列分块入门1-9,其中数列分块入门6就是定期重构

    你需要支持的操作是:单点插入,单点查询a[r]是多少,这道题目虽然数据随机,但是想要拿全分那肯定还是得要写个定期重构的。

    定期重构要干嘛?

    大佬的说法:将每根号 n 个操作分为一组,每次每组结束后将当前所有组的修改放 到数列中去 ------曹hl

    每次询问考虑剩余的修改操作对当前询问的影响 ,分次进行数列重构

    定期重构我认为就是在每单位个(根号n个)操作完成后对于分块数列进行重组,以维持算法的时间复杂度,保证结构稳定,(类似于“splay”)不会出现一种单块超级长的情况,否则毒瘤数据会卡得你的分块算法超时。

    定期重构怎么实现:

    你首先确定多少次操作后重构一次(大多数情况是根号n,但是具体情况要具体分析),然后每单位个修改操作之后就进行一次重构,维护数列的稳定(蛮容易的,看代码,唯一复杂点就是信息的复制)

    分块对列六---CODE

    #include <bits/stdc++.h>
    using namespace std;
    #define int long long
    int a[200005], n, l[1005], r[1005], block, t = 0, q = 0;
    int k[1000];
    int c[1000][1005], cc[1000][1005];
    int insert(int L, int R);
    int ask(int R);
    int deb();
    
    signed main() {
        cin >> n;
        for (int i = 1; i <= n; i++) cin >> a[i];
        int R;
        block = (int)(sqrt(n));  //确定块长
        while (R != n) {
            t++;
            l[t] = (t - 1) * block + 1;
            r[t] = min(t * block, n);
            R = r[t];
            k[t] = r[t] - l[t] + 1;
        }
        //初始化分块以及记录每一块里面元素个数
        for (int i = 1; i <= t; i++)
            for (int j = l[i]; j <= r[i]; j++) c[i][j - l[i] + 1] = a[j];
        //初始化每个块里面的元素
        for (int i = 1; i <= n; i++) {
            int op, L, c;
            cin >> op >> L >> R >> c;
            if (op == 0) {
                q++, insert(L, R);  //插入
                if (q == block)
                    deb(), q = 0;  //重构
            } else
                ask(R);
        }
        return 0;
    }
    int deb() {
        int num = 0, tt = 0, qq = 0, kk[1040], now1 = 1, now2 = 1;
        for (int i = 1; i <= t; i++) num += k[i];  //求出现在一共有多少个数,确定块长
        memset(kk, 0, sizeof(kk));
        int R = 0, z = 0;
        block = (int)(sqrt(num));  //确定块长,其实现在l和r都没用了,因为这个题目不需要
        while (R != num) {
            R++;
            qq++;
            if (R > block * now2)
                kk[now2] = block, now2++, qq = 1;
            z++;
            if (z > k[now1])
                now1++, z = 1;          //这是一个基本的指针用法
            cc[now2][qq] = c[now1][z];  //直接暴力的复制一份原来的数组
        }                               //"三指针法"(滑稽)
        if (kk[now2] == 0)
            kk[now2] = num - block * (now2 - 1);
        t = now2;
        for (int i = 1; i <= t; i++) k[i] = kk[i];
        for (int i = 1; i <= t; i++) {
            for (int j = 1; j <= k[i]; j++) c[i][j] = cc[i][j];
        }
        return 0;
    }
    int ask(int R) {
        int now = 1, ttt = 0;
        while (ttt + k[now] < R) ttt += k[now], now++;
        R -= ttt;
        cout << c[now][R] << endl;
        return 0;  //直接查询就行了,也运用了分块的思想,每次跳都跳一个块的长度
    }
    int insert(int L, int R) {
        int now = 1, ttt = 0;
        while (ttt + k[now] < L) ttt += k[now], now++;
        L -= ttt;  // L减去ttt后代表的就是插入当前块的编号了
        for (int i = k[now]; i >= L; i--) c[now][i + 1] = c[now][i];
        k[now]++;
        c[now][L] = R;  //直接挪,反正块长是根号n
        return 0;
    }
  • 相关阅读:
    第二阶段:团队开发Fooks第七天
    第二阶段:团队开发Fooks第六天
    第二阶段:团队开发Fooks第五天
    【POI每日题解 #9】SKA-Piggy Banks
    ac自动机
    【POI每日题解 #8】DYN-Dynamite
    vector
    【POI每日题解 #7】TES-Intelligence Test
    【POI每日题解 #6】KRA-The Disks
    DP
  • 原文地址:https://www.cnblogs.com/MYCui/p/13537981.html
Copyright © 2020-2023  润新知