摘自我的洛谷博客。

题目让我们求改变数字的最少次数,那我们转化一下,

求可以保留最多的数字个数 \(cnt\),再用 \(n\) 减一下就行,即 \(res = n - cnt\)。

我们先考虑两种暴力方法。

第一种暴力方法:

大体思路:因为要保留的最多,那么我们肯定要在众多等差数列中找能对应数字最多的那一个并保留下来。

首先,我们要知道一个概念。

对于这道题,那么我们可以暴力枚举公差 \(d\)(就是数组中相邻两项的差值都是 \(d\),并把题目中的每个 \(a[i]\) 对应的等差数列的最后一项 \(a[i] + d \times (n - i)\) 计算出来。


对于同一个公差 \(d\),如果不同位置计算出来的序列的最后一个值相同,那就说明它们属于同一个等差数列。

如果有 \(x\) 个数字计算出来的最后一个值都相同,那么采用其对应的等差数列作为修改后的数组,这 \(x\) 个数字是不需要改变的,只需要改变 \(n - x\) 个数字。

那我们可以想到,用桶记录计算出来的值 \(x\) 的出现次数 \(a[x]\)。如果某一次计算出来的值为 \(x\),那么可以将 \(a[x]\) 加 \(1\)。

如果 \(a[x]\) 是 \(a\) 中最大的元素,那么说明,以 \(a[x]\) 为结尾的等差数列中存在的元素数量最多,那么更改数字的数量也就减少了,只需要 \(n - a[x]\) 个元素。

这种方法的时间复杂度为 \(O(DN)\),\(D\) 为需要枚举的公差数量。

第二种暴力方法:

考虑动态规划,设 \(f[i][j]\) 表示以 \(a[i]\) 为等差数列最后一个元素的以 \(j\) 为公差的等差数列最多可以保留的数字个数

我们可以枚举上一个数字 \(a[k]\),如果它与 \(a[i]\) 在同一等差数列,那么有 \(f[i][j] = f[k][j] + 1\),表示又可以多保存一个数字了。

那这个序列的公差是多少呢?

这样考虑,中间有 \(i - k\) 个公差,差了 \(a[i] - a[k]\),那么公差就是\(\frac{a[i] - a[k]}{i - k}\)。

如果除不尽怎么办呢,那么这就说明 \(a[i]\) 和 \(a[k]\) 不能在同一个等差数列,不然公差为小数!

那 \(k\) 从哪里开始枚举呢?从 \(1\) 开始是不是太慢了?

这个等会儿讲。


那么为了平衡这两种暴力算法,我们可以这样办:

取输入的数列 \(a\) 的最大值 \(m\)。

我们只使用第一种方法枚举 \([0, \sqrt m]\) 的部分,时间复杂度为 \(O(n \sqrt m)\)。

我们使用第二种方法枚举 \([\sqrt m + 1, n]\) 的部分。

下面探讨第二种方法的时间复杂度,

首先回归到前面的问题,来探讨 \(k\)(\(i\) 的上一位数字在哪里) 从何处开始枚举,到哪里。

到哪里好解决,就是 \(i - 1\)。

而开始的地方,是 \(i - \sqrt m\)。为啥呢?

首先,因为公差 \(D\) 在 \([\sqrt m + 1, n]\) 之间,所以 \(D > \sqrt m\),那么我们计算差值 \(a[i] - a[k] = (a[k] + (i - k) \times D) - a[k] = (i - k) \times D > (i - k) \times \sqrt m\)。

首先假设 \(i, k\) 都在同一个等差数列中,如果 \(k+ \sqrt m < i\),那么\(a[i] - a[k] > (i - k) \times \sqrt m > \sqrt m \times \sqrt m = m\),这样的话,两数之差竟然比 \(m\) 还要大,不成立,

所以 \(k + \sqrt m \geq i\),也就是说 \(k\) 要从 \(i - \sqrt m\) 开始枚举。

所以,第二种方法的时间复杂度为 \(O(n \sqrt m)\)。

那么这个题的时间复杂度就为 \(O(n \sqrt m)\)。

代码:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <unordered_map> using namespace std; const int N = 100010; int n;
int a[N], maxx, sqrtmaxx; int u[(int)(N + N * sqrt(N))]; // 第一种暴力方法的桶
unordered_map<int, int> f[N]; // 第二种暴力方法的动态规划数组。 int max_keep() {
int ans = 0;
for (int d = 0; d <= sqrtmaxx; d++) { // 第一种暴力方法,枚举公差 D
for (int i = 1; i <= n; i++) {
ans = max(ans, ++u[a[i] + (n - i) * d]);
}
for (int i = 1; i <= n; i++) {
u[a[i] + (n - i) * d]--;
}
} for (int i = 1; i <= n; i++) { // 第二种暴力方法,动态规划
for (int j = max(1, i - sqrtmaxx); j < i; j++) {// j只用从 i - sqrt(m) 开始枚举
if ((a[i] - a[j]) % (i - j) == 0) {
int x = (a[i] - a[j]) / (i - j);
if (x <= sqrtmaxx) continue;
f[i][x] = max(f[i][x], f[j][x] + 1);
ans = max(f[i][x] + 1, ans);
}
}
} for (int i = 1; i <= n; i++) f[i].clear(); // 清空数组 return ans;
} int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr); cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i], maxx = max(maxx, a[i]);
sqrtmaxx = sqrt(maxx); int ans1 = 0, ans2 = 0;
ans1 = max_keep();
reverse(a + 1, a + n + 1); // 应对公差为负数的情况
ans2 = max_keep(); cout << n - max(ans1, ans2) << '\n';
return 0;
}

CF1654E Arithmetic Operations 题解的更多相关文章

  1. Continuity of arithmetic operations

    Arithmetic operations taught in elementary schools are continuous in the high level topological poin ...

  2. codechef Row and Column Operations 题解

    版权声明:本文作者靖心,靖空间地址:http://blog.csdn.net/kenden23/,未经本作者同意不得转载. https://blog.csdn.net/kenden23/article ...

  3. 等差数列Arithmetic Progressions题解(USACO1.4)

    Arithmetic Progressions USACO1.4 An arithmetic progression is a sequence of the form a, a+b, a+2b, . ...

  4. 多校第九场Arithmetic Sequence题解

    题目链接:http://acm.hdu.edu.cn/showproblem.php? pid=5400 题意:给定等差数列的差值d1,d2.问长度为n的数列中有多少个满足条件的子序列,条件为子序列中 ...

  5. 【CF1443F】Identify the Operations 题解

    原题链接 题意简介 建议去原题看.这题意我表达不清楚. 大概就是给你一个 n 的排列,现在要求你进行 m 次操作. 每次操作,你会在现有的排列中删去一个数,然后选择其左边或右边的一个与之相邻的数加入 ...

  6. [UCSD白板题] Maximize the Value of an Arithmetic Expression

    Problem Introduction In the problem, your goal is to add parentheses to a given arithmetic expressio ...

  7. Project Euler 93:Arithmetic expressions 算术表达式

    Arithmetic expressions By using each of the digits from the set, {1, 2, 3, 4}, exactly once, and mak ...

  8. Algebraic Kernel ( Arithmetic and Algebra) CGAL 4.13 -User Manual

    1 Introduction Real solving of polynomials is a fundamental problem with a wide application range. T ...

  9. Modular Arithmetic ( Arithmetic and Algebra) CGAL 4.13 -User Manual

    1 Introduction Modular arithmetic is a fundamental tool in modern algebra systems. In conjunction wi ...

  10. Algebraic Foundations ( Arithmetic and Algebra) CGAL 4.13 -User Manual

    理解: 本节主要介绍CGAL的代数结构和概念之间的互操作.与传统数论不同,CGAL的代数结构关注于实数轴的“可嵌入”特征.它没有将所有传统数的集合映射到自己的代数结构概念中,避免使用“数的类型”这一术 ...

随机推荐

  1. day07-OpenFeign-服务调用

    SpringCloud OpenFeign-服务调用 1.OpenFeign介绍 https://github.com/spring-cloud/spring-cloud-openfeign Open ...

  2. 从零开始学Vue(一)—— Vue.js 入门

    概述 vue.js作为现在笔记热门的JS框架,使用比较简单易上手,也成为很多公司首选的JS框架. 但是对于初学者可能学起来有些麻烦,所以推出<从零开始学Vue>系列博客,本系列计划推出19 ...

  3. 基于DotNetCoreNPOI封装特性通用导出excel

    基于DotNetCoreNPOI封装特性通用导出excel 目前根据项目中的要求,支持列名定义,列索引排序,行合并单元格,EXCEL单元格的格式也是随着数据的类型做对应的调整. 效果图: 调用方式 可 ...

  4. 3.2 构造器、this、包机制、访问修饰符、封装

    构造器 构造器:在实例化的一个对象的时候会给对象赋予初始值,因此我们可以通过修改构造器,来改变对象的初始值,构造器是完成对象的初始化,并不是创建对象 我们也可以创建多个构造器实现不同的初始化,即构造器 ...

  5. 一天吃透Redis面试八股文

    Redis连环40问,绝对够全! Redis是什么? Redis(Remote Dictionary Server)是一个使用 C 语言编写的,高性能非关系型的键值对数据库.与传统数据库不同的是,Re ...

  6. 微信小程序搜索不到腾讯服务路线规划插件的解决方法

    具体操作如下: 提示:主要内容都是按开发文档来写的 开发文档: 链接: https://lbs.qq.com/miniProgram/plugin/pluginGuide/routePlan 添加插件 ...

  7. 如何用 KMP 偏序 Z 函数

    KMP 算法求解字符串匹配的过程中 \(next\) 数组有着繁多的应用,主要是可以帮我们求 border. 然而用 \(s\) 串匹配 \(t\) 串产生的 \(f\) 数组应用相对较少. \(f\ ...

  8. ES6模块化之import的使用方式

    1.引入外部文件: <script src='文件路径' type='module'><!--PS:这个type="module" 必须要写,否则浏览器会报错-- ...

  9. [Flink] Flink Job运行状态正常,但日志中偶报“FlinkException: The file LOG does not exist on the TaskExecutor.”

    0 序言 Flink : 1.12 job start running time : 2022-12-27 17:40:47 problem throw time : 2023-05-11 16:41 ...

  10. 微信小程序客服、支付、定位、下拉加载功能

    一.客服功能 1.只要你微信小程序,后台添加了客服,引用以下button,就可以进入聊天(在小程序官网如何添加客服用户,请自行百度,谢谢) 2.通过按钮方式 <button open-type= ...