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

Description

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

要求回答 \(m\) 个询问。

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

\(1\leq 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-\lfloor\frac{a_i}{j}\rfloor×j\)。

那么枚举 \(k\),在所有满足 \(\lfloor\frac{a_i}{j}\rfloor=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(100n\log 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\n", ans);
    }
    fclose(stdin);
    fclose(stdout);
    return 0;
}

原文地址:https://www.cnblogs.com/cyf32768/p/12196042.html

时间: 2024-10-11 02:07:14

[日常训练]养花(分块+数论)的相关文章

「日常训练与知识学习」树的分块(王室联邦,HYSBZ-1086)

题意与分析 这题的题意就是树分块,更具体的看题目(中文题). 学习这一题是为了树的分块,为树上莫队做铺垫. 参考1:https://blog.csdn.net/LJH_KOQI/article/details/52326103 参考2:https://blog.csdn.net/popoqqq/article/details/42772237 注意到题目要求某块区域所有的点到根的路径上的点都属于该区域.因此不能够暴力地去dfs,每找到\(B\)个分一块是不可取的,因为无法保证联通性(一颗子树的下

[日常训练]yayamao的神题

Description $yayamao$是数学神犇,一天他在纸上计算起了$1/P$, 我们知道按照模拟除法可以得到准确解,例如$1/7=0.(142857),1/10=0.1(0)$.$yayamao$发现无论他如何模拟小数都会出现循环,现在$yayamao$想知道循环的长度以及循环出现之前,小数点后面的未循环的数字的位数.例如$1/15=0.0(6)$,那么它的循环长度为$1$,小数点后面的未循环的数字的位数为$1$;$1/4=0.25(0)$,那么它的循环长度为$1$,小数点后面的未循环的

[日常训练]最大M子段和

Description 在长度为的序列中选出段互不相交的子段,求最大字段和. Input 第一行两个整数. 第二行个整数. Output 一行一个整数表示最大值. Sample Input 5 2 10 -1 10 -1 10 Sample Output 29 HINT Solution 如果序列中正整数个数,直接取最大的个数的和即可. 将序列合并成若干个交错的正负段和,如-1,-2,3,4,-5,-6可以合并成-1-2,3+4,-5-6. 记录所有正数段之和,设正数段个数为,则需要把k段正数段

[日常训练]curves

Description 小同学的弟弟小昨天学习了数学中的一元二次函数,但是由于学业不精,他一个晚上都在缠着小问一元二次函数的极值问题,小烦不可耐,于是,想请你帮忙弄个程序来应付小.程序要完成以下任务: 给你个二次函数,记第个为:. 设函数. 请你求出的在区间上的最小值,结果保留位有效数字. Input 输入文件第一行是一个整数. 接下来行,每行个实数,之间有一个空格分隔. Output 输出一行一个实数,表示的在区间上的最小值. Sample Input 2 3 --2 1 2 -4 2 Sam

[日常训练]大灾难

Description 在一个生态圈中,食物链的维系是很重要的.食物链的断裂往往引起连锁反应,进而招致生态系统如同多米诺骨牌一样坍塌. 现在考虑一个简化模型.在一个生态系统中,有$N$种生物,它们分为两类:生产者与消费者.生产者通过这个系统之外的能量来生存,最常见的是植物的光合作用.而消费者需要"消费",也就是以其他生物为食物才可以生存.为了简化问题,我们假设所有消费者是可以分层的,高一层的消费者可能的食物来源都来自它的严格下层.生产者可以视为最下层的成员.当一种生物灭绝之后,依赖于它

[日常训练]变戏法

Description 一开始有$n$个只有颜色不同的小球.定义使用一次膜法的效果是重新排列第$l_i$个到第$r_i$个小球.给定了$n$个小球的初始状态和最终状态,以及$m$次膜法的范围$l_i,r_i$.判断是否可以从初始状态转移到最终状态. Input 第一行有一个整数$t$表示数据组数. 每组数据中, 第一行两个整数$n,m$,表示总共有$n$个小球,$m$次操作. 第二行$n$个整数$a_i$,表示初始状态. 第三行$n$个整数$b_i$,表示最终状态. 接下来$m$行,每行两个整数

[日常训练]training

Description 一条线上有栋楼,第栋楼有层,每层有1个价值为的物品. 可以花费1个单位时间完成以下3种移动: 1.在同一栋楼中向上或者向下走一层; 2.如果此刻在顶楼,可以通往1楼; 3.从当前楼移动到相邻楼的同层.如果相邻楼没有当前位置高,则会落到相邻楼的顶层. 初始时在第一栋楼的顶层,单位时间可以移动,拿去物品不需要时间,且一个物品被拿一次之后就会消失. 求能获得的最大的总价值. Input 第一行两个正整数. 以下行每行两个整数表示和. Output 输出一行一个整数表示最大的总价

[日常训练]article

Description 小今天来写作文啦! 小非常善于堆砌辞藻.在洋洋洒洒写了一长篇之后,小发现作文中很多段落都似曾相识.小认为,如果一段字符在文章开头,结尾和中间都出现过,那么这段字符就可以被认为是一个"经典段". 现在小给你一篇文章,他想知道最长的一段"经典段"长度是多少? 注意,这里要求经典串至少出现三次,分别作为文章的前缀,后缀,及既非前缀也非后缀. Input 一行,给出一个由小写字母组成的字符串. Output 输出满足题目要求的非空子串,如果不存在这样

[日常训练]school

Description 众所周知,家离学校很远.于是,每天算准了时间出发,以保证能在上课铃响前 秒到达学校. 不幸的是,市最近正在修路.这就导致有些路可能无法通行,因而可能导致迟到. 不打算改变他的出发时间,现在他告诉你他通过每一条路的时间,他想要知道如果某条路被维修了,那么他是否能避免迟到? Input 第一行输入两个正整数,分别表示点数(路口)和边数(路). 第二行输入两个正整数,表示家标号为,学校标号为. 接下来行,每行三个整数,表示有一条连接的道路,走过该路所需的时间为. 接下来一个整数