第一题

【题目描述】

给定n,求所有小于等于n的数,与n的最大公约数之和。

【输入】

输入包含一个非负整数n。

【输出】

输出一个整数,为求和的结果。

【输入样例】

10

【输出样例】

27

【提示】

对于40%的数据点,1<=n<=10^5。

对于60%的数据点,1<=n<=10^7。

对于100%的数据点,1<=n<=10^9。

好,这道题我现场莫比乌斯反演AC,正解是欧拉函数0ms,反演跑的慢些,100ms...反正我也不会欧拉函数。

下面由我来讲解如何A题。

按照套路,设

则有

推出来三个式子,目测最下面那个好一些,就选它了!

miu(1e9)显然不能直接求。

我预处理出来1e7以内的miu,多余的就按照定义暴力计算。

复杂度上界是

但是跑起来飞快...

考场
 1 #include <cstdio>
 2 typedef long long LL;
 3 const int N = 10000010;
 4 
 5 int miu[N], n, p[N], top;
 6 bool vis[N];
 7 
 8 inline int F(int x) {
 9     return n / x;
10 }
11 
12 inline void getmiu(int b) {
13     miu[1] = 1;
14     b = b >= N ? N - 1 : b;
15     for(int i = 2; i <= b; i++) {
16         if(!vis[i]) {
17             p[++top] = i;
18             miu[i] = -1;
19         }
20         for(int j = 1; j <= top && i * p[j] <= b; j++) {
21             vis[i * p[j]] = 1;
22             if(i % p[j] == 0) {
23                 break;
24             }
25             miu[i * p[j]] = -miu[i];
26         }
27     }
28     return;
29 }
30 
31 inline int u(int x) {
32     if(x < N) {
33         return miu[x];
34     }
35     int t = 0;
36     for(int i = 1; i <= top && p[i] * p[i] <= x; i++) {
37         if(x % p[i] == 0) {
38             t++;
39             x /= p[i];
40             if(x % p[i] == 0) {
41                 return 0;
42             }
43         }
44     }
45     if(x > 1) {
46         t++;
47     }
48     return t & 1 ? -1 : 1;
49 }
50 
51 inline LL solve(int i) {
52     LL ans = 0;
53     for(int d = 1; d * d <= i; d++) {
54         if(i % d == 0) {
55             ans += i / d * u(d);
56             if(d * d < i) {
57                 ans += d * u(i / d);
58             }
59         }
60     }
61     return ans * F(i);
62 }
63 
64 int main() {
65     //freopen("a.in", "r", stdin);
66     //freopen("a.out", "w", stdout);
67     LL ans = 0;
68 
69     scanf("%d", &n);
70     getmiu(n);
71     for(int i = 1; i * i <= n; i++) {
72         if(n % i == 0) {
73             ans += solve(i);
74             if(i * i < n) {
75                 ans += solve(n / i);
76             }
77         }
78     }
79     printf("%lld", ans);
80     return 0;
81 }
AC代码

然后尝试中间那个式子。

注意F(i)当i不是n的约数的时候值为0,上面那个式子调用的i都是n的约数,所以没加判断可以过。

第二个式子光荣牺牲,60分TLE

 1 #include <cstdio>
 2 typedef long long LL;
 3 const int N = 10000010;
 4 
 5 int miu[N], n, p[N], top;
 6 bool vis[N];
 7 
 8 inline int F(int x) {
 9     if(n % x) {
10         return 0;
11     }
12     return n / x;
13 }
14 
15 inline void getmiu(int b) {
16     miu[1] = 1;
17     b = b >= N ? N - 1 : b;
18     for(int i = 2; i <= b; i++) {
19         if(!vis[i]) {
20             p[++top] = i;
21             miu[i] = -1;
22         }
23         for(int j = 1; j <= top && i * p[j] <= b; j++) {
24             vis[i * p[j]] = 1;
25             if(i % p[j] == 0) {
26                 break;
27             }
28             miu[i * p[j]] = -miu[i];
29         }
30     }
31     return;
32 }
33 
34 inline int u(int x) {
35     if(x < N) {
36         return miu[x];
37     }
38     int t = 0;
39     for(int i = 1; i <= top && p[i] * p[i] <= x; i++) {
40         if(x % p[i] == 0) {
41             t++;
42             x /= p[i];
43             if(x % p[i] == 0) {
44                 return 0;
45             }
46         }
47     }
48     if(x > 1) {
49         t++;
50     }
51     return t & 1 ? -1 : 1;
52 }
53 
54 inline LL solve(int d) {
55     LL ans = 0;
56     for(int i = 1; i * d <= n; i++) {
57         ans += 1ll * u(i) * F(i * d);
58     }
59     return ans;
60 }
61 
62 int main() {
63     LL ans = 0;
64 
65     scanf("%d", &n);
66     getmiu(n);
67     for(int d = 1; d * d <= n; d++) {
68         if(n % d == 0) {
69             ans += d * solve(d);
70             if(d * d < n) {
71                 int t = n / d;
72                 ans += t * solve(t);
73             }
74         }
75     }
76     printf("%lld", ans);
77     return 0;
78 }
代码二

第一个式子和第二个本质相同。

总结:这更像是数学题...重要的是分辨哪个跑得快。如果不能确定可以打出来比较一下。

 

posted @ 2018-08-02 15:46  garage  阅读(124)  评论(0编辑  收藏  举报