UVa 11426 - GCD - Extreme (II)

Published on 2016-02-29

题目地址

描述

给定 n(1<n4000000)n(1 < n \le 4000000),请你计算下面式子的值:

i=1n1j=i+1ngcd(i,j)\displaystyle\sum\limits_{i = 1}^{n-1}\sum\limits_{j = i + 1}^{n}\gcd(i, j)

样例输入

10
100
200000
0

样例输出

67
13015
143295493160

分析

拿到式子,好像没思路。那就搞一搞和式,来个内外层交换:

i=1n1j=i+1ngcd(i,j)=j=2ni=1j1gcd(i,j)\displaystyle\sum\limits_{i = 1}^{n-1}\sum\limits_{j = i + 1}^{n}\gcd(i, j) = \displaystyle\sum\limits_{j = 2}^{n}\sum\limits_{i = 1}^{j - 1}\gcd(i, j)

这样爽多了。我们把里层的这玩意设成 f(n)=i=1n1gcd(i,n)f(n) = \sum\limits_{i = 1}^{n - 1}\gcd(i, n),这样就会固定一个参数 nn,看起来可搞。
g(i,n)g(i, n) 为满足 gcd(x,n)=i(1xn)\gcd(x, n) = i (1\le x\le n)xx 个数,那么 f(n)=g(i,n)if(n) = \sum g(i, n) * i。由于 gcd(x,n)=i(1xn)\gcd(x, n) = i(1\le x\le n),两边同时除去 iigcd(xi,ni)=1(1xn)\gcd(\frac{x}{i}, \frac{n}{i}) = 1(1\le x\le n),即 g(i,n)=ϕ(ni)g(i, n) = \phi(\frac{n}{i}),即与 nn 的最大公约数为 ii 的数个数即为与 ni\frac{n}{i} 互素的数个数。
f(n)f(n) 的过程变成了枚举 nn 的约数的过程,但约数很多,不好直接枚举。换一个思路,我们直接枚举约数,用类似筛法的过程去刷各个 nn,刷的时候累加即可,复杂度 O(nloglogn)O(n\log\log n)
本题的要点在于各种转换,是个不错的思维题。

代码

//  Created by Sengxian on 2/29/16.
//  Copyright (c) 2016年 Sengxian. All rights reserved.
//  UVa 11426 gcd + phi + 思维
#include <algorithm>
#include <iostream>
#include <cctype>
#include <climits>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <vector>
#include <stack>
#define br putchar('\n');
using namespace std;
inline int ReadInt() {
    int n = 0, ch = getchar();
    while(!isdigit(ch)) ch = getchar();
    while(isdigit(ch)) n = n * 10 + ch - '0', ch = getchar();
    return n;
}
const int maxn = 4000000 + 3;
long long phi[maxn], f[maxn], s[maxn];
void process() {
    memset(phi, 0, sizeof(phi));
    phi[1] = 1;
    for(int i = 2; i < maxn; ++i) if(!phi[i])
        for(int j = i; j < maxn; j += i) {
            if(!phi[j]) phi[j] = j;
            phi[j] = phi[j] / i * (i - 1);
        }
    memset(f, 0, sizeof(f));
    for(int i = 1; i < maxn; ++i)
        for(int j = i + i; j < maxn; j += i)
            f[j] += i * phi[j / i];
    s[0] = s[1] = 0;
    for(int i = 2; i < maxn; ++i)
        s[i] = s[i - 1] + f[i];
}

int main() {
    process();
    int n;
    while(n = ReadInt(), n)
        printf("%lld\n", s[n]);
    return 0;
}

附加输入

50
66158
1946
51240
138
318024
8907
843
2883707
17640
8
99295
697
54869
162
3172435
2
93659
7484
126
1558
6
174
3707
1616
171
16035
41094
4000000
371
161
702886
108
10771
1215
47483
1313
304
3186
253380
15
9509
118
57167
174
811
374408
19048
174
285
116
0

附加输出

2725
14208321806
8237287
8319160954
26343
376584743998
209185885
1365217
36535367082249
885271650
38
33221891524
906034
9601187847
37729
44509691503795
1
29401976509
144732617
21686
5114484
20
43782
32574957
5534028
42421
723868691
5237427576
71887733425828
230227
37000
1958618608264
15341
312593179
2997908
7091734021
3544968
149396
23608437
234613937007
172
240197159
18496
10463800744
43782
1255629
528897503942
1040608982
43782
129325
17855