传送门

题目大意

1
2
3
4
5
6
7
小菜的妹妹小诗就要读小学了!正所谓计算机要从娃娃抓起,小菜决定在幼儿园最后一段轻松的时间里教妹妹编程。
  小菜刚教完gcd即最大公约数以后,一知半解的妹妹写了如下一段代码:
   sum:=0;
   for i:=1 to n-1 do
   for j:=i+1 to n do sum:=sum+gcd(i,j)

  显然这个程序的效率是很低的,小明打算写一个更强的程序,在求出sum的同时比妹妹跑的更快。

输入格式

1
2
第一行一个整数t,即表示有t组数据
  接下来t行,每行一个整数n

输出格式

1
t行,每行一个整数,表示n所对应的sum值

样例输入

1
2
3
2
10
100

样例输出

1
2
67
13015

数据范围

1
2
3
4
【数据规模】
  20%数据t≤100,n≤100
  40%数据t≤1000,n≤2000
  100%数据t≤10000,n≤1000000

题解

题目目的是求11~nn内任意两个数的gcdgcd之和,先放着。

假设gcd(n,i)=kgcd(n,i)=k,则gcd(nk,ik)=1gcd(\frac{n}{k},\frac{i}{k})=1

即假设gcd(nk,x)=1gcd(\frac{n}{k},x)=1,则gcd(n,xk)=kgcd(n,x*k)=k

因为kk的取值是nn的因子,

所以gcd(n,x×k)=kgcd(n,x\times k)=kxx的个数×k\times k为答案

所以题目转换成了inni×φ(n/i)\sum_{i\mid n}^{n} i\times \varphi(n/i)

就设fi=gcd(1,n)+gcd(2,n)+...+gcd(n,n)f_i=gcd(1,n)+gcd(2,n)+...+gcd(n,n)

那么ansi=ansi1+fians_i=ans_{i-1}+f_i

先用线筛筛出φ(i)\varphi(i)OI-Wiki上讲的挺清楚的,不会可以看看)

筛的过程中顺便处理掉ffansans

答案就是ansnans_n

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <cstdio>
#define N 1000005
using namespace std;
int pr[70000],cnt;
int T,n,phi[N],t,mx;
long long sum[N],f[N];
bool p[N];int a[N];
void init(int mx)
{
for (int i=2;i<=mx;++i) {
if (!p[i]) pr[++cnt]=i,phi[i]=i-1;
for (int j=1;j<=cnt;++j) {
int x=i*pr[j];if (x>mx) break;
p[x]=1,phi[x]=phi[i]*(pr[j]-(i%pr[j]>0));
}
}for (int i=1;i<=mx;++i) {
for (int j=i+i;j<=mx;j+=i)
f[j]+=i*phi[j/i];
sum[i]=f[i]+sum[i-1];
}return ;
}
int main()
{
scanf("%d",&T);
for (int i=1;i<=T;++i)
scanf("%d",&a[i]),
mx=(a[i]>mx)?a[i]:mx;
init(mx);
for (int i=1;i<=T;++i)
printf("%lld\n",sum[a[i]]);
}