CF1900D Small GCD

Link

这是一个需要欧拉反演的题目

首先,可以知道只和数字之间的大小有关,数列的顺序无关,那么就可以首先排一个序方便解决该问题。

根据欧拉函数的性质,知道n=d|nϕ(n)

那么我们每次先确定中间的数aj,然后根据公式,得他它得贡献是i=1j1gcd(ai,aj)

用欧拉函数处理一下这个东西后面的gcd,然后得到i=1j1d|aid|ajϕ(d)

交换一下求和的顺序,得到d|aji=1j1d|aiϕ(d),显然的,后面就是这个因数d的出现次数

所以可以化简为d|ajcntdϕ(d)

这个东西就可以很快的求出来了。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<stack>
#include<set>
#include<map>
#include<vector>
#include<ctime>
#include<bitset>
#define int long long
using namespace std;
int phi[100005];
int not_prime[100005];
int prime[100005];
int p;
int a[100001];
long long ans;
void Eul(){
	phi[1]=1;
	not_prime[1]=0;
	for(int i=2;i<=100000;++i){
		if(!not_prime[i]){
			prime[++p]=i;
			phi[i]=i-1;
		}
		for(int j=1;j<=p&&i*prime[j]<=100000;++j){
			not_prime[i*prime[j]]=1;	
			if(i%prime[j]==0){
				phi[i*prime[j]]=phi[i]*prime[j];
				break;
			}
			phi[i*prime[j]]=phi[i]*phi[prime[j]];
		}
	}
}
int t;
int n;
int cnt[100005];
signed main(){
	Eul();
	scanf("%lld",&t);
	while(t--){
		memset(cnt,0,sizeof(cnt));
		scanf("%lld",&n);
		for(int i=1;i<=n;++i)
			scanf("%lld",&a[i]);
		sort(a+1,a+n+1);
		ans=0;
		for(int i=1;i<=n;++i){
			long long tem=0;
			for(int j=1;j*j<=a[i];j++){
				if(a[i]%j!=0) continue;
				tem+=phi[j]*cnt[j];
				cnt[j]++;
				if(j*j!=a[i]){
					tem+=phi[a[i]/j]*cnt[a[i]/j];
					cnt[a[i]/j]++;
				}
			}
			ans+=tem*(n-i);
		}
		printf("%lld\n",ans);
	}
	return 0;
}
posted @   Simex  阅读(12)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
历史上的今天:
2020-12-12 P2911 [USACO08OCT]Bovine Bones G(py)
2020-12-12 P1614 爱与愁的心痛(py)
点击右上角即可分享
微信分享提示