分块

此博客仅供分块思想入门使用,如果需要进一步地了解分块,请前往笔者的 这篇博文

基本概念

分块是一种经过优化的暴力算法,它将一个长度为 \(n\) 的数组分成尽量平均的若干块,每块的长度称为块长。因为通常情况下把区间分成块长为 \(\sqrt{n}\) 的块,所以分块算法的时间复杂度通常为 \(O(m\sqrt{n})\),具体时间复杂度根据所分的块长而定。

算法思想

假如把区间分成若干个相等的块,原本在区间上进行的操作就可以变成在若干个块上进行的操作。我们通常将一个长度为 \(n\) 的数组分成 \(\sqrt{n}\) 块块长为 \(\sqrt{n}\) 的块,如果 \(n\) 不是完全平方数,则我们还会分出一个多余的块。假设待操作的区间为 \([l, r]\),我们首先判断 \(l\) 和 \(r\) 是否在同一个块内。假如它们在同一块内,我们就直接对 \(l\) 和 \(r\) 所在的块进行操作。否则,我们将区间 \([l, r]\) 分成至少两块:最左边的一块,中间的若干个被完整覆盖的块,最右边的一块。

因为最左边的一块可能没有被完全覆盖,所以我们需要枚举左边的那一块被 \([l, r]\) 覆盖的部分,直接对每一个元素进行操作,最右边的一块同理。对于中间被完整覆盖的部分,我们可以直接对整块进行操作。我们利用线段树中的 \(lazy\) 思想,直接对整块进行加上某个数之类的操作。

分块不仅可以维护区间和,还可以维护区间中有多少个小于 \(k\) 的数、区间的最大公因数等等。分块的思想其实就是 优雅的暴力,只是特判了被整体覆盖的块。

参考代码

例题链接

#include <cstdio>
#include <cmath>
using namespace std;

const int maxn = 5e5 + 5;
const int maxm = 2e3 + 5;

int n;
int a[maxn], st[maxm], ed[maxm];
int bel[maxn], lazy[maxm];

inline int read()
{
    int res = 0, flag = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9')
    {
        if (ch == '-')
            flag = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9')
    {
        res = res * 10 + ch - '0';
        ch = getchar();
    }
    return res * flag;
}

inline void update(int l, int r, int x)
{
    if (bel[l] == bel[r])
        for (register int i = l; i <= r; i++)
            a[i] += x;
    else
    {
        for (register int i = l; i <= ed[bel[l]]; i++)
            a[i] += x;
        for (register int i = st[bel[r]]; i <= r; i++)
            a[i] += x;
        for (register int i = bel[l] + 1; i < bel[r]; i++)
            lazy[i] += x;
    }
}

signed main()
{
    int opt, l, r, c;
    n = read();
    int block = sqrt(n);
    int size = ceil(n * 1.0 / block);
    for (register int i = 1; i <= size; i++)
    {
        st[i] = (i - 1) * block + 1;
        ed[i] = i * block;
    }
    ed[size] = n;
    for (register int i = 1; i <= n; i++)
    {
        a[i] = read();
        bel[i] = (i - 1) / block + 1;
    }
    for (register int i = 1; i <= n; i++)
    {
        opt = read();
        l = read();
        r = read();
        c = read();
        if (opt == 0)
            update(l, r, c);
        else
            printf("%d\n", a[r] + lazy[bel[r]]);
    }
    return 0;
}
上一篇:Codeforces1446 B. Catching Cheaters(dp,最长公共子序列)


下一篇:NOIP 模拟5 T3 big