SP5971 LCMSUM - LCM Sum
题意
- 求(sum_{i=1}^nlcm(i,n)).
- 数据范围:(Tleq 300000,nleq 1000000).
思路
首先知道(lcm(i,n)=frac{i*n}{gcd(i,n)}),所以有原式:
[nsum_{i=1}^nfrac{i}{gcd(i,n)}
]
一般到这里都要枚举一下(gcd(i,n))。
[nsum_{d|n}sum_{i=1}^nfrac{i}{d}[gcd(i,n)=d]
]
(gcd(i,n)=d),那么必然有(i)是(d)的倍数。
所以枚举(id)
[nsum_{d|n}sum_{i=1}^frac{n}{d}i[gcd(i,n/d)=1]\
]
已知
[sum_{i=1}^ni[gcd(i,n)=1]=frac{varphi(n)*n}{2}
]
转换一下:
[nsum_{d|n}frac{varphi(frac{n}{d})frac{n}{d}}{2}
]
(d)和(frac{n}{d})都是成对出现的,所以再改一下:
[nsum_{d|n}frac{varphi(d)d}{2}
]
这里由于询问特别大,不能每次都枚举约数。
所以用倍数法预处理出所有答案。
时间复杂度为n乘一个到n的调和级数求和,大概在(nlnn)。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 10;
int n;
int phi[maxn], primes[maxn], cnt;
ll ans[maxn];
bool vis[maxn];
void get_eulers(int n)
{
phi[1] = 1;
for(int i = 2; i <= n; i++)
{
if(!vis[i])
{
primes[++cnt] = i;
phi[i] = i-1;
}
for(int j = 1; primes[j] <= n/i; j++)
{
vis[primes[j]*i] = 1;
if(i % primes[j] == 0)
{
phi[primes[j]*i] = phi[i]*primes[j];
break;
}
else phi[primes[j]*i] = phi[i]*(primes[j]-1);
}
}
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n/i; j++)
ans[i*j] += 1ll*j*phi[j]/2;
for(ll i = 1; i <= n; i++)
ans[i] = ans[i]*i+i;
}
inline void solve()
{
scanf("%d", &n);
printf("%lld
", ans[n]);
}
int main()
{
get_eulers(maxn-5);
int T; scanf("%d", &T);
while(T--) solve();
return 0;
}