洛谷P5682 次大值 题解
题目链接:https://www.luogu.com.cn/problem/P5682
题目描述
Alice 有 \(n\) 个正整数,数字从 \(1 \sim n\) 编号,分别为 \(a_1,a_2, \cdots , a_n\)。
Bob 刚学习取模运算,于是便拿这 \(n\) 个数进行练习,他写下了所有
\(a_i\) mod \(a_j (1 \le i,j \le n \wedge i \neq j)\)
的值,其中 mod 表示取模运算。
Alice 想知道所有的结果中,严格次大值是多少。将取模后得到的所有值进行去重,即相同的结果数值只保留一个,剩余数中第二大的值就称为严格次大值。
解题思路
首先我们要排除无关因素带来的影响。
这里很明显的一个无关因素就是重复的元素。
我们假设存在 \(a_i = a_j\) 且 \(i \neq j\),则对于任何一个 \(k \neq i \wedge k \neq j\):
- \(a[i]\) mod \(a[k] = a[j]\) mod \(a[k]\)
- \(a[k]\) mod \(a[i] = a[k]\) mod \(a[j]\)
所以数值相同的两个元素是等价的。
我们可以给 \(a1\) 到 \(a_n\) 从小到大排序并去重。
我们假设去重后还剩下 \(m\) 个元素,且这些元素满足 \(a_1 \lt a_2 \cdots \lt a_m\)。
则我们可以分 \(m=1\)、\(m=2\)、\(m \gt 2\) 三种情况分开讨论:
m = 1
当 \(m=1\) 时,数组中只有一个元素:
- 如果 \(n = 1\),则不存在答案,输出 \(-1\);
- 如果 \(n \gt 1\),则说明数组中所有的元素都是相同的,则任意 \(a_i\) mod \(a_j\) 的结果都是 \(0\),所以只存在一个最大值 \(0\),不存在次大值,同样也输出 \(-1\)。
m = 2
当 \(m=2\) 时,只有两个候选答案 \(a_1\) mod \(a_2\) 和 \(a_2\) mod \(a_1\),又因为 \(a_1\) mod \(a_2 = a_1\),\(a_2\) mod \(a_1 \lt a_1\)(余数必然小于除数),所以次大值肯定是 \(a_2\) mod \(a_1\)。
m > 2
当 \(m \gt 2\) 时,可以确定的是最大值是 \(a_{m-1}\) mod \(a_m\)。
次大值应该是以下两者的较大值:
- \(a_{m-2}\) mod \(a_m\);
- \(a_{m}\) mod \(a_{m-1}\)。
详细证明
为了方便讲述起见,我接下来用 \(a \% b\) 来表示 \(a\) mod \(b\),并且我们这里只证明 \(m \gt 2\) 的情况。
这里有一个非常重要的基础性质:
性质1:余数永远小于除数!
我们接下来会反复运用到这个性质1进行分析。
首先我们证明 \(a_{m-1} \% a_m\) 是最大值。
因为 \(a_{m-1} \% a_m = a_{m-1}\)。
证:
我们把 \(a_i \% a_j\) 的组合分为 \(2\) 部分:
第一部分:\(j=m\)
此时 \(i \in [1, m-1]\),
\(a_1 \% a_m \lt a_2 \%a_m \lt \cdots \lt a_{m-1} \% a_m\)
所以第一部分的最大值是 \(a_{m-1} \% a_m = a_{m-1}\)
第二部分:\(j \neq m\),
此时,除数最大是 \(a_{m-1}\),根据性质1,可得:这一部分的所有答案都小于 \(a_{m-1}\)。
据此可得:最大值即为 \(a_{m-1} \% a_m = a_{m-1}\)。
其次我们要求次大值
次大值其实就是除了 \(a_{m-1}\) 以外的最大值。
第一部分里面除了 \(a_{m-1} \% a_m\) 以外的最大值就是 \(a_{m-2} % a_m = a_{m-2}\) 了。
第二部分我们可以将它拆分成两小部分:
第2.1部分:\(j = m-1\) 的部分,这一部分的是 \(a_{m-2}\) 和 \(a_m \% a_{m-1}\) 的较大值;
第2.2部分:\(j \le m-2\) 的部分,根据性质1,这一部分的所有答案都 \(\lt a_{m-2}\)。
综上所述,我们可以发现,次大值的竞争者只有 \(2\) 个值:\(a_{m-2}\) 或者 \(a_m \% a_{m-1}\)。
所以此时次大值即为 \(\max(a_{m-2}, a_m \% a_{m-1})\)。
另附上一张图片方便理解:
实现代码如下:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 200020;
int n, m, a[maxn], ans;
int main() {
cin >> n;
for (int i = 1; i <= n; i ++) cin >> a[i];
sort(a+1, a+1+n);
m = unique(a+1, a+1+n) - a - 1;
if (m == 1) ans = -1;
else if (m == 2) ans = a[2] % a[1];
else ans = max(a[m-2] % a[m], a[m] % a[m-1]);
cout << ans << endl;
return 0;
}