• AtCoder | ABC 125 Person Editorial


    开始补AtCoder的数学题了,练下思维

    AB两道都很简单,看懂题就OK。

    C,D稍微麻烦一些

    Problem C: GCD On Blackboard

    为了解决此问题,我们需要了解最大公约数(GCD)的某些属性。
    第一点是,GCD是可交换的(GCD(a,b)= GCD(b,a))。
    第二个是3个数字的GCD是关联的(GCD(a,GCD(b,c))= GCD(GCD(a,b),c))。
    最后,在不失一般性的前提下,如果a和b是2个正数且a <= b,则GCD(a,b)<= a。 换句话说,2个数字的GCD最多等于较小的数字。

    回到问题陈述,我们有一个数字列表,可以修改列表中的一个数字,然后用另一个数字替换它,以使列表的GCD最大化。 让我们将关于GCD的已知知识应用于此问题:

    假设我们的列表 (A = A_1,A_2,A_3,...,A_n),我们希望修改索引 (A_i)。 根据关联规则,我们知道:GCD(A1,A2,A3,...,Ai,...,An)可以重新排序为:GCD(A1,A2,A3,...,An,Ai)。 换句话说,我们可以计算除 (A_i)以外的所有数字的GCD,然后使用(A_i)计算结果的GCD的结果。 令g为除(A_i)以外的所有数字的GCD。 现在我们希望最大化GCD(g,Ai)。 由于对(A_i)进行简化后我们可以获得的最大GCD为g,因此答案为g。

    因此,我们知道在位置i处可获得的最大gcd是除(A_i)以外的所有数字的gcd。 如果我们在每个 (i) 处计算这些gcd值,则所有结果的最大值是修改列表中的数字后可能的最大GCD。
    那么我们将如何实现该算法? 为方便起见,让我们定义(R_i)(L_i)的两个功能,如下所示:

    [R_{n + 1} = 0\R_i = GCD(R_{i + 1},A_i)\L_0= 0\L_i = GCD(L_{i - 1},A_i) ]

    (L_i)代表A1…Ai的gcd,(R_i)代表Ai…An的GCD。 如果我们希望找到除Ai以外的所有元素的GCD,则可以找到Mi = GCD(Li-1,Ri + 1)。 这表示除Ai以外的所有数字的GCD。
    我们可以预先计算R和L。最后,我们可以找到Mi的每个值,然后计算出最大值,这就是我们的答案。 示例代码如下(复杂度为(O(n))):

    const int N = 100000 + 5;
    int main() {
        ios_base::sync_with_stdio(false), cin.tie(0);
        // L_i = gcd(A_1, A_2, A_3, ... A_i)
        // R_i = gcd(A_i, A_{i+1}, A_{i+1}, ..., A_n)
        int n;
        ll a[N], L[N], R[N];
        cin >> n;
    
        for (int i = 1; i <= n; ++i) {
            cin >> a[i];
            L[i] = R[i] = a[i];
        }
    
        // precomputing L[i]
        for (int i = 1; i <= n; i++) {
            L[i] = __gcd(L[i], L[i - 1]);
        }
        // precomputing R[i]
        for (int i = n; i > 0; i--) {
            R[i] = __gcd(R[i], R[i + 1]);
        }
    
        // computing each m_i and the max value
        int res = 0;
        for (int i = 1; i <= n; i++) {
            int m = __gcd(L[i - 1], R[i + 1]);
            res = max(m, res);
        }
    
        cout << res;
        return 0;
    }
    

    Problem D: Flipping Signs

    让我们考虑元素A1,A2,A3,…,An的列表。 我们被允许翻转任意两个连续元素Ai和Ai + 1的符号。 需要注意的一个有用观察结果是(除了最后一个元素),我们可以翻转任何Ai的符号而不会影响Ai-1。
    但是我们如何利用这个事实呢? 好吧,我们知道我们可以使从i = 1到i = n-1的每个元素对于任何列表都是正的。 由于我们不在乎操作数量,因此这种贪婪的方法效果很好。
    给定任何数字A列表,我们将能够使用此方法使除最后一个元素之外的所有Ai均为正。 最后一个元素将为负或正。 让我们分别处理这种情况。
    所以现在我们可以将最后一个元素设为正数或负数。 如果为正,则所有元素均为正,列表的总和就是我们的答案。 如果它是负数,那么我们需要检查一些变化以获得最大的总和结果。 首先,我们需要检查最后两个元素中哪个更大。 由于我们可以使最后一个元素为正,而使An-1为负。 实际上,我们可以使任何一个Aj为负(j <n),以使最后一个元素为正。 考虑三个连续的正a,b,c元素。 我们首先可以翻转a和b的符号,这将使它们变为负数。 在下一步中,我们可以翻转b的符号,这将使b变为正,而c变为负。 这将使a变为负数,我们可以再次翻转c的符号使其变为正数,依此类推。 我们可以链接此操作,该操作将只留下a为负数,其余元素保持正值(最后一个元素除外,该元素将从负数变为正数)。
    因此,我们有了算法最后一步的配方:我们检查范围A1…An-1中的最小元素。 将此最小元素称为m。 如果m <An,则将m的符号翻转为负,然后使An为正。 否则,我们将m保持为正。 这个清单的总和就是我们的答案。
    Bellow是此解决方案的C ++实现。 注意,该解决方案与列表的其余部分分开处理An-1,An的情况,但是它与上述算法相同。

    using ll = long long;
    const int N = 1e5 + 10;
    int main() {
        ios_base::sync_with_stdio(false), cin.tie(0);
        int n, A[N];
        // store the sum of the result in a long long pair
        ll s = 0;
        cin >> n;
        for (int i = 1; i <= n; ++i) cin >> A[i];
    
        for (int i = 1; i <= n - 2; ++i) {
            if (A[i] < 0) A[i] *= -1, A[i + 1] *= -1;
            s += A[i];
        }
    
        // if n is 2, we only need to consider those
        // Also , if the last 2 elements are either both positive
        // or both negative then we simply flip their signs too
        if ((n == 2) or (A[n] > 0 and A[n - 1] > 0) or (A[n] < 0 and A[n - 1] < 0))
            s += max(A[n - 1] + A[n], -A[n - 1] - A[n]);
        else {
            // find the min element in the list (excluding last 2 elements)
            int* mni = min_element(A + 1, A + n - 2);
            int mnv = *mni;
    
            // if the minimum value in the remaining list is larger than
            // the smaller value of the last 2 elements, then we should
            // just keep it positive
            if (mnv > min(abs(A[n - 1]), abs(A[n]))) {
                s += max(A[n - 1] + A[n], -A[n - 1] - A[n]);
            }
            // otherwise, we can make that value negative, and include the
            // last 2 elements as positive elements
            else {
                s -= mnv * 2;
                s += abs(A[n - 1]);
                s += abs(A[n]);
            }
        }
        cout << s << "
    ";
    
        return 0;
    }
    

    The desire of his soul is the prophecy of his fate
    你灵魂的欲望,是你命运的先知。

  • 相关阅读:
    Quick-cocos2d-x3.3 Study (十)--------- 添加动画
    Quick-cocos2d-x3.3 Study (九)--------- 为物体添加物理特性
    Quick-cocos2d-x3.3 Study (八)--------- 物理世界
    Quick-cocos2d-x3.3 Study (七)--------- 滚动的背景
    Quick-cocos2d-x3.3 Study (六)--------- 跳转场景和过渡动画
    反射机制(实例化Class)对象
    简单查询
    Scott用户的四张表:
    sqlplus
    日期处理类
  • 原文地址:https://www.cnblogs.com/RioTian/p/14413764.html
Copyright © 2020-2023  润新知