传送门
题目大意
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 2 3 4
| 【数据规模】 20%数据t≤100,n≤100 40%数据t≤1000,n≤2000 100%数据t≤10000,n≤1000000
|
题解
题目目的是求1~n内任意两个数的gcd之和,先放着。
假设gcd(n,i)=k,则gcd(kn,ki)=1
即假设gcd(kn,x)=1,则gcd(n,x∗k)=k
因为k的取值是n的因子,
所以gcd(n,x×k)=k 中x的个数×k为答案
所以题目转换成了∑i∣nni×φ(n/i)
就设fi=gcd(1,n)+gcd(2,n)+...+gcd(n,n)
那么ansi=ansi−1+fi
先用线筛筛出φ(i)(OI-Wiki上讲的挺清楚的,不会可以看看)
筛的过程中顺便处理掉f和ans
答案就是ansn
代码
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]]); }
|