2 条题解

题解与来源题共享。已有题解默认通过;新增或编辑后需由来源域管理员审核。待审核、驳回题解仅作者和审核管理员可见。来源域:system

  • 0
    @ 2025-7-6 19:22:14

    一、题意抽象

    • 给定长度为 nn 的正整数序列 {ai}i=1n\{a_i\}_{i=1}^n,且题目对每个公差 d=1,2,…,nd=1,2,\dots,n 构造一个等差数列

      bi=i⋅d,i=1,2,…,n. b_i = i\cdot d,\quad i=1,2,\dots,n.
    • 定义

      Sd=∑i=1n[ ai∣bi ], S_d = \sum_{i=1}^n [\,a_i \mid b_i\,],

      其中 [ ⋅ ][\,\cdot\,] 是指示函数,“1” 表示条件成立,“0” 表示不成立。也就是说,SdS_d 就是数有多少个下标 ii 使得 aia_i 能整除 bi=i⋅db_i=i\cdot d。

    • 要求最终答案

      ∑d=1nSd. \sum_{d=1}^n S_d.

    朴素做法:对每个 dd 枚举所有 ii,判断 i⋅d mod ai=ˉ0i\cdot d \bmod a_i\==0。最坏是 O(n2)O(n^2),当 nn 达到 10510^5 时不可行。


    二、数学化简

    二、观察与数学化简

    判断条件

    $$a_i \mid (i\cdot d) \;\Longleftrightarrow\; i\cdot d \equiv 0 \pmod{a_i} \;\Longleftrightarrow\; d \equiv 0 \pmod{\frac{a_i}{\gcd(a_i,i)}}.$$

    令

    $$g_i = \gcd(a_i,\,i), \quad A_i' = \frac{a_i}{g_i}.$$

    则上式等价于

    Ai′∣d.A_i' \mid d.

    也就是说,对于固定的 ii,只有当 dd 是 Ai′A_i' 的倍数时,[ ai∣i⋅d ][\,a_i \mid i\cdot d\,] 才为 1。

    因此

    Sd  =  ∑i=1n[ Ai′∣d ],S_d \;=\;\sum_{i=1}^n \bigl[\,A_i'\mid d\,\bigr],

    我们要求

    $$\sum_{d=1}^n S_d =\sum_{d=1}^n \sum_{i=1}^n \bigl[\,A_i'\mid d\,\bigr] =\sum_{i=1}^n \sum_{d=1}^n \bigl[\,A_i'\mid d\,\bigr].$$

    对于固定的 ii,在 1≤d≤n1\le d\le n 中满足 Ai′∣dA_i'\mid d 的一共有 $\displaystyle \left\lfloor \tfrac{n}{A_i'}\right\rfloor$ 个。

    于是

    $$\sum_{d=1}^n S_d =\sum_{i=1}^n \Bigl\lfloor \frac{n}{A_i'}\Bigr\rfloor =\sum_{i=1}^n \Bigl\lfloor \frac{n}{\,a_i/g_i\,}\Bigr\rfloor.$$

    三、从思路到代码

    1. 读取输入:nn,数组 a[1…n]a[1\ldots n]。

    2. 遍历每个下标 ii:

      • 计算 g=gcd⁡(i, ai)g = \gcd(i,\,a_i);
      • 令 A′=ai/gA' = a_i / g;
      • 累加答案 += ⌊n/A′⌋\lfloor n / A' \rfloor。
    3. 输出累加结果。

    此算法中每步 gcd⁡\gcd 和一次除法、一次整除计数,都是 O(log⁡n)O(\log n) 级别,整体 O(nlog⁡n)O(n\log n),可轻松处理 n=105n=10^5。


    四、代码

    #include <bits/stdc++.h>
    using namespace std;
    using ll = long long;
    
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
    
        ll n;
        cin >> n;
    
        ll ans = 0;  // 最终答案累加器
    
        // 逐个读取 a_i,并即时累加对答案的贡献
        for (ll i = 1; i <= n; i++) {
            ll ai;
            cin >> ai;
    
            // 1. 计算 g = gcd(i, ai)
            ll g = std::gcd(i, ai);
    
            // 2. 化简后得到 A' = ai / g
            //    只有当 d 是 A' 的倍数时,(i, d) 配对才算 valid
            ll Ap = ai / g;
    
            // 3. 在 d = 1..n 范围内,A' 的倍数共有 floor(n/Ap) 个
            ans += n / Ap;
        }
    
        // 输出最终累加值
        cout << ans << "\n";
        return 0;
    }
    

    信息

    ID
    176
    时间
    1000ms
    内存
    256MiB
    难度
    普及−
    标签
    递交数
    112
    已通过
    40
    上传者