COMPFEST 13 Finals Online Mirror, cf-1575G. GCD Festival 题解
作者:互联网
title: 'codeforces-COMPFEST 13 Finals Online Mirror, cf-1575G. GCD Festival'
date: 2021-11-15 17:04:50
tags: [math, number theory]
mathjax: true
题意
给定一个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=0}^{n} \phi(d) \sum _{i=d,i+=d}^{n}\sum _{j=d,j+=d}^{n}\gcd(a_i,a_j) \\ &=\sum _{d=0}^{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=0}^{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=0}^{n} \phi(d) \sum _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\) 当前带来的贡献
公式推导如下
个人感觉写的有点赘余,每一步都只有一小步的化简,但是考虑到数学公式的难以理解,还是写的很冗长,还望和我一样的初学者看的更明白些
\[\begin{aligned} Ans&=\sum_{d=0}^{n} \phi(d) \sum _x \sum _{i=d,i+=d}^{n}\sum _{j=d,j+=d}^{n} [x|a_i\land x|a_j] \\ &=\sum_{d=0}^{n} \phi(d) \sum _x (\sum _{i=d,i+=d}^{n}[x|a_i] \cdot \sum _{j=d,j+=d}^{n} [x|a_j]) \\ &=\sum_{d=0}^{n} \phi(d) \sum _x (\sum _{i=d,i+=d}^{n}[x|a_i] \cdot \sum _{i=d,i+=d}^{n} [x|a_i]) \\ &=\sum_{d=0}^{n} \phi(d) \sum _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;
}
标签:phi,land,GCD,cdot,题解,sum,13,枚举,aligned 来源: https://www.cnblogs.com/LacLic/p/15557637.html