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

定期重构

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

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;
}
原文地址:https://www.cnblogs.com/MYCui/p/13537981.html