[日常训练]养花(分块+数论)

Posted cyf32768

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[日常训练]养花(分块+数论)相关的知识,希望对你有一定的参考价值。

Description

给定一个长度为 (n) 的数组 (a)

要求回答 (m) 个询问。

对于每个询问,给出 (l,r,k),求 (max_{i=l}^{r}left{ a_i \% k ight})

(1leq n,a_i,l,r,k leq 10^5+1)

Solution

分块,块数 (100)

(f[i][j]) 表示第 (i) 块的数 (\%j) 的最大值,考虑怎么求 (f[i][j])

显然有 (a_i\%j=a_i-lfloorfrac{a_i}{j} floor×j)

那么枚举 (k),在所有满足 (lfloorfrac{a_i}{j} floor=k)(a_i) 中取 (a_i-k×j) 的最大值。

这相当于找满足 (a_i<(k+1)×j) 的最大的 (a_i)

对于每块,预处理 (b[x]) 表示这一块 (≤ x) 的数当中的最大值。

然后枚举 (j,k)(f[i][j]=max(b[(k+1)×j-1]-k×j))

假设 (n,a_i) 同阶,那么预处理时间复杂度 (O(100nlog n)),常数非常小。

询问复杂度 (O(1000m))

Code

#include <bits/stdc++.h>

using namespace std;

template <class t>
inline void read(t & res)
{
    char ch;
    while (ch = getchar(), !isdigit(ch));
    res = ch ^ 48;
    while (ch = getchar(), isdigit(ch))
    res = res * 10 + (ch ^ 48);
}

const int e = 100005, o = 105, m = 100001;
int f[o][e], n, a[e], bl[e], s, br[e], b[e], bel[e], q;

int main()
{
    int i, j, k, now = 0, l, r;
    read(n); read(q); s = n / 100 + 1;
    for (i = 1; i <= n; i++) read(a[i]);;
    for (i = 1; i <= n; i = j + 1)
    {
        bl[++now] = i; br[now] = j = min(n, now * s);
        for (k = i; k <= j; k++) bel[k] = now;
    }
    for (i = 1; i <= now; i++)
    {
        l = bl[i]; r = br[i];
        for (j = 1; j <= m; j++) b[j] = 0;
        for (j = l; j <= r; j++) b[a[j]] = a[j];
        for (j = 1; j <= m; j++) b[j] = max(b[j], b[j - 1]);
        for (j = 1; j <= m; j++)
        for (k = 0; k <= m; k += j)
        f[i][j] = max(f[i][j], b[min(m, k + j - 1)] - k);
    } 
    while (q--)
    {
        read(l); read(r); read(k);
        int pl = bel[l], pr = bel[r], ans = 0;
        if (pl == pr)
        {
            for (i = l; i <= r; i++) ans = max(ans, a[i] % k);
        }
        else
        {
            for (i = pl + 1; i < pr; i++) ans = max(ans, f[i][k]);
            for (i = l; i <= br[pl]; i++) ans = max(ans, a[i] % k);
            for (i = bl[pr]; i <= r; i++) ans = max(ans, a[i] % k);
        }
        printf("%d
", ans);
    }
    fclose(stdin);
    fclose(stdout);
    return 0;
}

以上是关于[日常训练]养花(分块+数论)的主要内容,如果未能解决你的问题,请参考以下文章

余数(数论分块)

数论分块 数学

bzoj 3309 DZY Loves Math —— 莫比乌斯反演+数论分块

数论分块之整除分块

Luogu5307 [COCI2019] Mobitel 数论分块递推

《夜深人静写算法》数论篇 - (23) 整数分块