• 洛谷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 eq j))

    的值,其中 mod 表示取模运算。

    Alice 想知道所有的结果中,严格次大值是多少。将取模后得到的所有值进行去重,即相同的结果数值只保留一个,剩余数中第二大的值就称为严格次大值。

    解题思路

    首先我们要排除无关因素带来的影响。

    这里很明显的一个无关因素就是重复的元素。

    我们假设存在 (a_i = a_j)(i eq j),则对于任何一个 (k eq i wedge k eq 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 eq 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;
    }
    
  • 相关阅读:
    【转载】天才与柱子
    Windows Phone 7 隔离存储空间资源管理器
    (收藏)让你平步青云的十个谈话技巧
    (收藏)《博客园精华集》设计模式分册
    (收藏)C#开源资源大汇总
    (收藏)生活物语
    (收藏)C# ORM/持久层框架
    (收藏)《博客园精华集》AJAX与ASP.NET AJAX部分
    小型项目总结之五
    VS 打包完美解决方案
  • 原文地址:https://www.cnblogs.com/quanjun/p/12401293.html
Copyright © 2020-2023  润新知