题目链接:bzoj2226
题目大意:
多组数据。给定n,求
∑ni=1lcm(i∗n)
。
1 <= T <= 300000,1 <= n <= 1000000
题解:
线性筛、欧拉函数
∑i=1nlcm(i∗n)=n∑i=1nigcd(i,n)
省略前面的,套路化简:
n∑i=1n∑d|i,d|nid[gcd(i,n)=d]
交换枚举倍数和约数即
n∑d|n∑i=1⌊nd⌋i[gcd(i,nd)=1]
(后面的 i 是
又
nd
跟
d
是一样的
所以就把式子弄成
来看后面 ∑di=1i[gcd(i,d)=1] 这串,其含义就是1到d中与d互质的数的和。而若有gcd(i,n)=1的话必有gcd(n-i,n)=1,即这些数都是成对出现的(除了1)。所以每对的和就是d,那么这些数的和就是 ϕ(d)d2 ,还要处理下1的,所以我就把式子写成了 ϕ(d)d+12 ,毕竟整数类型下除法去尾取整嘛。
又有两个积性函数相乘也是积性函数,所以 ϕ(d)d 是个积性函数。
于是就可以线性筛预处理 ϕ(d)d 。
你可以每次询问的时候在线算。但是 O(nlogn) 预处理答案会更快。
嗯..为什么是 O(nlogn) 呢?dalao说是均摊log的。
我代码里直接用 phi 表示 ϕ(d)d 了。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
#define N 1000100
bool ispri[N];
LL cnt,pri[N/4],phi[N],ans[N];
void Eular(LL lim)
{
cnt=0;phi[1]=1;
for (LL i=2;i<=lim;i++)
{
if (!ispri[i]) {pri[++cnt]=i;phi[i]=(i-1)*i;}
for (LL j=1;j<=cnt && pri[j]*i<=lim;j++)
{
LL k=i*pri[j];
ispri[k]=true;
if (i%pri[j]==0)
{
phi[k]=pri[j]*phi[i]/i*k;
break;
}
phi[k]=(pri[j]-1)*phi[i]/i*k;
}
}
}
int main()
{
//freopen("a.in","r",stdin);
//freopen("a.out","w",stdout);
LL T,lim,i,j,n;
scanf("%lld",&T);
lim=1000000;Eular(lim);
for (i=1;i<=lim;i++) ans[i]=0;
for (i=1;i<=lim;i++)
{
LL sm=(phi[i]+1)/2;
for (j=i;j<=lim;j+=i) ans[j]+=sm;
}
while (T--)
{
scanf("%lld",&n);
printf("%lld\n",n*ans[n]);
}
return 0;
}