链接:https://ac.nowcoder.com/acm/contest/392/D
题目描述
因为月月是个信息学高手,所以她也给华华出了一题,让他求:
∑Ni=1igcd(i,N)∑i=1Nigcd(i,N)
但是因为这个式子实在太简单了,所以月月希望华华对N=1,2,…,n各回答一次。华华一脸懵逼,所以还是决定把这个问题丢给你。
输入描述:
一个正整数n。
输出描述:
输出n行,第i行表示N=i时的答案。
示例1
输入
复制
6
输出
复制
1
2
4
6
11
11
备注:
1≤n≤1061≤n≤106
解析
首先有一个公式,
与n互质而且小于n的所有数的和为phi[n]*n/2
数组phi为欧拉函数
对于每一个N
gcd(i,N)==t
我们要考虑t取值不同时的所有贡献
而t只能是N的因子,对于N的因子x0,x1,x2,x3…来说
如果t=x0,即gcd(i,N)=x0
可以转化为gcd(i/x0,N/x0)=1
也就是说与N/x0互质的数再乘上x0就是i(分子),但式子本身两边除了x0,所以不用再乘x0直接就是对于结果的贡献
这对N产生的贡献就是phi[N/x0]*(N/x0)/2
如果我们对于每个N都枚举t肯定会超时,但是反过来想的话,我们可以枚举所有的t(因子),这样就会缩减时间复杂度。
coding
#include <bits/stdc++.h>
#define ll long long
#define pb push_back
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,b,a) for(int i=b;i>=a;i--)
using namespace std;
const int N=1e6+10;
ll phi[N],vis[N],pri[N];
ll ans[N];
int main()
{
//ios::sync_with_stdio(false);
ll n;
scanf("%lld",&n);
phi[1]=1;
for(int i=2;i<=n;i++){
if(!vis[i]){
pri[++pri[0]]=i;
phi[i]=i-1;
}
for(int j=1;j<=pri[0]&&pri[j]*i<=n;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]*phi[pri[j]];
}
}
}
for(ll i=1;i<=n;i++){
for(ll j=i;j<=n;j+=i){
ans[j]+=phi[j/i]*(j/i)/2ll;
}
}
for(int i=1;i<=n;i++){
printf("%lld\n",ans[i]+1ll);
}
return 0;
}