COMPFEST 13 Finals Online Mirror, cf-1575G. GCD Festival 题解

题意

给定一个n长度的数组a,要你计算

\[\sum_{i=1}^{n} {\sum_{j=1}^{n} {\gcd(a_i, a_j) \cdot \gcd(i, j)}} \]

思路

莫比乌斯反演(其实我觉得这里叫欧拉反演更合适些)

\[gcd(i,j) = \sum _{d|i \land d|j} \phi(d) \]

代入原式

\[Ans=\sum_{i=1}^{n} {\sum_{j=1}^{n} {\gcd(a_i, a_j) \cdot \sum _{d|i \land d|j} \phi(d)}} \]

这里更换贡献计算顺序,枚举d来叠加答案的贡献

不过,在此之前,我们先约定一个符号

\[\sum_{i=d,i+=d}^{n} f(d) \]

\(i=d\) 表示 \(i\) 的初始值 \(i+=d\) 表示 \(i\) 的步长(每次\(+d\)),上面那个 \(n\) 表示循环上界(\(i\leq n\)),相当于for(int i=d;i<=n;i+=d)

下面继续计算答案

\[\begin{aligned} Ans&=\sum _{i=1}^{n} {\sum _{j=1}^{n} {\gcd(a_i, a_j) \cdot \sum _{d|i \land d|j} \phi(d)}} \\ &=\sum _{d=1}^{n} \phi(d) \sum _{i=d,i+=d}^{n}\sum _{j=d,j+=d}^{n}\gcd(a_i,a_j) \\ &=\sum _{d=1}^{n} \phi(d) \sum _{i=d,i+=d}^{n}\sum _{j=d,j+=d}^{n}\sum _{x|a_i \land x|a_j} \phi(x) \\ \end{aligned} \]

同理,我们枚举因子 \(x\),考虑什么时候\(a_i,a_j\)对答案有贡献

\[\begin{aligned} Ans&=\sum_{d=1}^{n} \phi(d) \sum _{i=d,i+=d}^{n}\sum _{j=d,j+=d}^{n}\sum _{x|a_i \land x|a_j} \phi(x) \\ &=\sum_{d=1}^{n} \phi(d) \sum _x \phi(x) \sum _{i=d,i+=d}^{n}\sum _{j=d,j+=d}^{n} [x|a_i\land x|a_j] \end{aligned} \]

其中,\(\sum _{i=d,i+=d}^{n}\sum _{j=d,j+=d}^{n} [x|a_i\land x|a_j]\) 表示当 \(x\) 整除 \(a_i\)\(x\) 整除 \(a_j\) 时,有1个贡献。那么我们就可以统计数组 \(a\) 中能被 \(x\) 整除的数有多少个,求平方(组合数学中的乘法原理)即是 \(x\) 当前带来的贡献

公式推导如下

个人感觉写的有点赘余,每一步都只有一小步的化简,但是考虑到数学公式的难以理解,还是写的很冗长,还望和我一样的初学者看的更明白些

(公式中,\(\phi(x)\) 抄漏了, 感谢 猫萌不萌 的指正)

\[\begin{aligned} Ans&=\sum_{d=1}^{n} \phi(d) \sum _x \phi(x) \sum _{i=d,i+=d}^{n}\sum _{j=d,j+=d}^{n} [x|a_i\land x|a_j] \\ &=\sum_{d=1}^{n} \phi(d) \sum _x \phi(x) (\sum _{i=d,i+=d}^{n} \sum _{j=d,j+=d}^{n} [x|a_i] \cdot [x|a_j]) \\ &=\sum_{d=1}^{n} \phi(d) \sum _x \phi(x) (\sum _{i=d,i+=d}^{n}[x|a_i] \cdot \sum _{j=d,j+=d}^{n} [x|a_j]) \\ &=\sum_{d=1}^{n} \phi(d) \sum _x \phi(x) (\sum _{i=d,i+=d}^{n}[x|a_i] \cdot \sum _{i=d,i+=d}^{n} [x|a_i]) \\ &=\sum_{d=1}^{n} \phi(d) \sum _x \phi(x) (\sum _{i=d,i+=d}^{n}[x|a_i])^2 \end{aligned} \]

好了,到这里,我们就可以考虑如何枚举 \(x\) 了,显然枚举到 \(n\) 不太实际(复杂度\(O(n^2)\)),我们考虑转而枚举每个元素的因子,这样的话就是 \(O(n\cdot log\ n \cdot max(|d(a_i)|)\) ,其中\(n\)来自于枚举 \(d\)\(log\ n\)来自于每次d枚举过程中选择的数(相关证明类似埃氏筛,与log的级数有关),\(|d(a_i)|\) 表示 \(a_i\) 的因子个数,也就是再去枚举每个数的因子(事先需要筛一次因数),因子个数大概也是log级别,这里由于知识面的不广暂不给出相关证明

代码

#include <bits/stdc++.h>
using namespace std;
using i64 = long long;

const i64 MOD = 1e9+7;
const int MX = 1e5+6;

vector<int> ph(MX);
vector<int> d[MX];

void get_phi() {
    for(int i=0;i<MX;++i) {
        ph[i] = i;
    }
    for(int i=2;i<MX;++i) {
        if(ph[i]==i)
            for(int j=i;j<MX;j+=i) {
                ph[j] -= ph[j]/i;
            }
    }

}
void get_divisor() {
    for(int i=1;i<MX;++i) {
        for(int j=i;j<MX;j+=i) {
            d[j].push_back(i);
        }
    }
}

int main(int argc, char const *argv[])
{
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr); cout.tie(nullptr);

    get_phi();
    get_divisor();

    int n;
    cin >> n;
    vector<i64> a(n+1);
    for(int i=1;i<=n;++i) {
        cin >> a[i];
    }

    i64 ans = 0;
    vector<int> cnt(MX);
    for(int i=1;i<=n;++i) {
        i64 s1=0;

        for(int j=i;j<=n;j+=i) {
            for(int e : d[a[j]]) {
                ++cnt[e];
            }
        }

        for(int j=i;j<=n;j+=i) {
            for(int e : d[a[j]]) {
                if(cnt[e]) {
                    i64 s2=cnt[e];
                    cnt[e] = 0;
                    s2 *= s2;
                    s2 %= MOD;
                    s2 *= ph[e];
                    s2 %= MOD;

                    s1 += s2;
                    s1 %= MOD;
                }
            }
        }

        ans += ph[i] * s1;
        ans %= MOD;
    }
    cout << ans;

    return 0;
}
posted @ 2021-11-15 18:17  LacLic  阅读(115)  评论(4编辑  收藏  举报