CF1575G GCD Festival

\(\sum\sum gcd(i,j) \times gcd(a_i,a_j)\)

考虑枚举这个 \(gcd(i,j)\)

\(\sum_d \varphi(d)\sum_{i|d}\sum_{j|d} gcd(a_i,a_j)\)

考虑后者等同于计算\(\sum_i\sum_j gcd(a_i,a_j)\)

我们考虑枚举约数 \(d\),那么会 \(d | gcd\) 的情况为 \((\sum[d | a_i]) ^ 2\)

考虑我们要求的是最大公约数,而非约数。

但是我们有\(x = \sum_{d|x}\varphi(x)\)

我们在情况数前加上一个系数即可。

转而求
\(\sum_d \varphi(d)\sum_t \varphi(t) (\sum[t | a_{k * d}]) ^ 2\)

那么预处理出因数,我们枚举 \(d\) ,然后 \(O(nln)\) 的遍历 \(a_i\) ,然后一次 \(d(n)\) 的处理一个数。

那么复杂度为预处理\(O(nln)\),计数复杂度\(O(\sum \lfloor\frac{n}{i} \rfloor d(i)) \leq O(Max{d(u)}nln)\)

#include<iostream>
#include<cstdio>
#include<map>
#include<vector>
#include<queue>
#define ll long long 
#define N 100005
#define mod ((ll)1e9 + 7)

int n,a[N];
int phi[N];
int vis[N];
int Cnt,pri[N];

std::vector<int>Q[N];

inline void sieve(){
	phi[1] = 1;
	for(int i = 2;i < N;++i){
		if(!vis[i])
		phi[i] = i - 1,pri[++Cnt] = i;
		for(int j = 1;pri[j] * i < N && j <= Cnt;++j){
			vis[i * pri[j]] = 1;
			if(i % pri[j] == 0){
				phi[i * pri[j]] = phi[i] * pri[j];
				break;
			}else{
				phi[i * pri[j]] = phi[i] * (pri[j] - 1);
			}
		}	
	}
	for(int i = 1;i < N;++i)
	for(int j = 1;j * i < N;++j)
	Q[i * j].push_back(i);
}

ll ans = 0;

int cnt[N];

#define p(x) ((x >= mod) ? x - mod : x)

inline void del(int u){
//	std::cout<<u<<std::endl;
	ll now = 0;
	for(int i = u;i <= n;i += u){
		for(int j = 0;j < Q[a[i]].size();++j){
			now = p(now + p(p((p(cnt[Q[a[i]][j]] * 2) % mod + 1)) * phi[Q[a[i]][j]]) % mod);
			cnt[Q[a[i]][j]] ++ ;
		}
	}
	for(int i = u;i <= n;i += u){
		for(int j = 0;j < Q[a[i]].size();++j){
			cnt[Q[a[i]][j]] = 0 ;
		}
	}	
	ans = p(ans + 1ll * phi[u] * p(now) % mod);
}

int main(){
	sieve();
	scanf("%d",&n);
	for(int i = 1;i <= n;++i)
	scanf("%d",&a[i]);
	for(int i = 1;i <= n;++i)
	del(i);
	std::cout<<ans<<std::endl;
}
原文地址:https://www.cnblogs.com/dixiao/p/15609607.html