LOJ#2085 循环之美

解:首先看这个纯循环到底是什么玩意.....

经过一番打表,发现纯循环小数就是分母与进制互质的既约分数。

 1 #include <bits/stdc++.h>
 2 
 3 std::bitset<1001> vis;
 4 
 5 inline bool check(int x, int y) { /// x / y
 6     //printf("x = %d y = %d \n", x, y);
 7     vis.reset();
 8     int rest = x % y;
 9     if(!rest) return true;
10     int op = rest;
11     vis[op] = 1;
12     //printf("op = %d \n", op);
13     while(true) {
14         rest *= 10;
15         int r = rest % y;
16         //printf("r  = %d \n", r);
17         if(vis[r]) {
18             //printf("return %d = %d \n", r, op);
19             return r == op;
20         }
21         vis[r] = 1;
22         rest = r;
23     }
24     return false;
25 }
26 
27 int gcd(int a, int b) {
28     if(!b) return a;
29     return gcd(b, a % b);
30 }
31 
32 int main() {
33 
34     int n;
35     scanf("%d", &n);
36 
37     for(int i = 0; i <= n; i++) {
38         for(int j = 1; j <= n; j++) {
39             if(gcd(i, j) == 1) printf("%d ", (int)check(i, j));
40             else printf("  ");
41         }
42         puts("");
43     }
44 
45     return 0;
46 }
打表程序

那么就有了一个很显然的O(nmlogV)的做法...直接暴力枚举然后检验。实测24分。

 1 #include <bits/stdc++.h>
 2 
 3 int gcd(int a, int b) {
 4     if(!b) return a;
 5     return gcd(b, a % b);
 6 }
 7 
 8 int main() {
 9     int n, m, k, ans = 0;
10     scanf("%d%d%d", &n, &m, &k);
11     if(1ll * n * m > 1000000000) return 0;
12     for(int i = 1; i <= m; i++) {
13         if(gcd(k, i) > 1) continue;
14         for(int j = 1; j <= n; j++) {
15             ans += gcd(i, j) == 1;
16         }
17     }
18     printf("%d\n", ans);
19     return 0;
20 }
24分暴力

然后发现最里面那句话有点像phi...仔细思考之后发现不是。

现在开始鬼畜时间...推柿子。

其中有两步转化,分别是把[x=1]用∑μ代替和把[1=(id,k)]用[1=(i,k)][1=(d,k)]代替。

于是考虑最后这个式子,发现有个东西[1=(a,k)],于是设这个东西为g(x),它的前缀和为f(x)。又令F为μ·g的前缀和。

那么答案就是下式:

这个东西显然可以分块一波。后两项可以O(1)算,前面的可以线性预处理。于是我们有个O(n)的算法。可以获得84分。

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

接下来补个k = 2 / 3的部分分。严格来说应该能应付k是质数的情况,然而后面k都是合数....

推倒过程几乎跟上面一样,最后得到这个东西:

考虑怎么计算后面那个求和符号。当d和k不互质(d是k的倍数)的时候,答案显然是0。否则令t = i / d,答案就是与k互质的t个个数。这个东西我们可以用maxt - maxt / k来O(1)计算。

 

 1 #include <bits/stdc++.h>
 2 
 3 typedef long long LL;
 4 const int N = 20000010;
 5 
 6 int miu[N], p[N], top, f[N], phi[N], n, m, k, F[N];
 7 bool vis[N];
 8 
 9 inline void getp(int n) {
10     phi[1] = miu[1] = 1;
11     for(int i = 2; i <= n; i++) {
12         if(!vis[i]) {
13             p[++top] = i;
14             miu[i] = -1;
15             phi[i] = i - 1;
16         }
17         for(int j = 1; j <= top && i * p[j] <= n; j++) {
18             vis[i * p[j]] = 1;
19             if(i % p[j] == 0) {
20                 //miu[i * p[j]] = 0;
21                 phi[i * p[j]] = phi[i] * p[j];
22                 break;
23             }
24             phi[i * p[j]] = phi[i] * (p[j] - 1);
25             miu[i * p[j]] = -miu[i];
26         }
27     }
28     return;
29 }
30 
31 int gcd(int a, int b) {
32     if(!b) return a;
33     return gcd(b, a % b);
34 }
35 
36 inline int calc(int d) {
37     if((d % k) == 0) {
38         return 0;
39     }
40     d = m / d;
41     return d - d / k;
42 }
43 
44 inline void prework() {
45     for(int i = 1; i <= k; i++) {
46         f[i] = f[i - 1] + (gcd(i, k) == 1);
47         F[i] = F[i - 1] + (f[i] - f[i - 1]) * miu[i];
48     }
49     int len = std::min(n, m);
50     for(int i = k + 1; i <= len; i++) {
51         f[i] = f[k] * (i / k) + f[i % k];
52         F[i] = F[i - 1] + (f[i] - f[i - 1]) * miu[i];
53     }
54     return;
55 }
56 
57 inline int getf(int x) {
58     if(x <= k) return f[x];
59     return f[k] * (x / k) + f[x % k];
60 }
61 
62 int main() {
63     LL ans = 0;
64     scanf("%d%d%d", &n, &m, &k);
65     if(1ll * n * m < 1e9) {
66         for(int i = 1; i <= m; i++) {
67             if(gcd(k, i) > 1) continue;
68             for(int j = 1; j <= n; j++) {
69                 ans += gcd(i, j) == 1;
70             }
71         }
72         printf("%lld\n", ans);
73         return 0;
74     }
75     if((k == 2 || k == 3) && (n <= 10000)) {
76         getp(n);
77         int len = std::min(n, m);
78         for(int i = 1; i <= len; i++) {
79             ans += 1ll * miu[i] * (n / i) * calc(i);
80         }
81         printf("%lld\n", ans);
82         return 0;
83     }
84     /*if(n < N) {
85         getp(n);
86         prework();
87         int len = std::min(n, m);
88         for(int i = 1, j; i <= len; i = j + 1) {
89             j = std::min(n / (n / i), m / (m / i));
90             /// [i, j]
91             ans += 1ll * (F[j] - F[i - 1]) * (n / i) * getf(m / i);
92         }
93         printf("%lld\n", ans);
94     }*/
95     return 0;
96 }
40分代码

 

posted @ 2019-04-24 09:52  huyufeifei  阅读(175)  评论(0编辑  收藏  举报
试着放一个广告栏(虽然没有一分钱广告费)

ReadEra 阅读书籍

『Flyable Heart 応援中!』 HHG 高苗京铃 闪十PSS 双六 電動伝奇堂 章鱼罐头制作组 はきか 祝姬 星降夜