Skip to content

整除分块 ​

又叫数论分块。

⌊ni⌋ 只有 O(n) 种数值,不同数值之间连续,且容易计算:[li,ri]→⌊nli⌋,ri=⌊n⌊ni⌋⌋,li=ri−1+1

对于求和式:S(n)=∑i=1nf(i)g(⌊ni⌋),根据整除分块可得:S(n)=∑i=1mg(⌊nli⌋)∑j=lirif(j),其中 m=O(n),总时间复杂度:O(nT(n)),其中 T(n) 为计算 f(n) 前缀和的时间复杂度。

点击展开代码
cpp
for (int i = 2, j; i <= n; i = j + 1) {
    j = n / (n / i);
    // 求 [i, j] 的贡献
}

多维整除分块 ​

形如:

∑i=1n∑j=1mFj(⌊aji⌋)

此时,分块区间 li=min{⌊aji⌋}。

容易发现每个 aj 产生 O(aj) 个区间,总时间复杂度为:O(mn),但是区间有相同部分,常数较小。

点击展开代码
cpp
for (int i = 1; i <= m; i++)
    maxn = max(maxn, a[i]);
for (int i = 2, j; i <= maxn; i = j + 1) {
    j = maxn;
    for (int k = 1; k <= m; k++) {
        j = min(j, a[k] / (a[k] / i));
    }
    // 求 [i, j] 的贡献
}

二次整除分块 ​

形如:

∑i=1n⌊ni2⌋

时间复杂度:O(n3)

点击展开代码
cpp
for (int i = 1, j; i <= m; i = j + 1) {
    auto now = n / (i * i);
    j = sqrtl(n / now);
   	// 求 [i, j] 的贡献
}

杜教筛 ​

杜教筛用于求解一些积性函数前缀和:S(n)=∑i=1nf(i)。

构造积性函数 g(n)。

∑i=1n(f∗g)(n)=∑i=1n∑d∣if(d)g(id)=∑i=1ng(d)S(⌊nd⌋)

特别地:

g(1)S(n)=∑i=1n(f∗g)(n)−∑i=2ng(d)S(⌊nd⌋)

令 T(n) 为求解 S(n) 的时间复杂度,结合整除分块,那么:

T(n)=H(n)+∑d∣nnG(d)T(d)

预处理 S(1)…S(m),时间复杂度:O(m+nm),m=n23 时最优,为:O(n23)。

递归过程中状态需要记忆化,通常使用哈希表维护。

特别地,若求一次 S(n),容易发现状态只有 O(n) 个,分为 [1,n] 和 (n,n] 分别储存即可。

点击展开代码
cpp
int sum_euler(int n) {
    if (n <= 2e6)
        return euler[n];
    if (mem1.count(n))
        return mem1[n];
    int res = (1 + n) * n / 2;
    for (int i = 2, j; i <= n;) {
        j = n / (n / i);
        res -= (j - i + 1) * sum1(n / i);
        i = j + 1;
    }
    return mem1[n] = res;
}
int sum_mu(int n) {
    if (n <= 2e6)
        return mu[n];
    if (mem2.count(n))
        return mem2[n];
    int res = 1;
    for (int i = 2, j; i <= n;) {
        j = n / (n / i);
        res -= (j - i + 1) * sum2(n / i);
        i = j + 1;
    }
    return mem2[n] = res;
}

min_25 筛 ​