zoukankan      html  css  js  c++  java
  • 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 
    ", 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 
    ", op);
    13     while(true) {
    14         rest *= 10;
    15         int r = rest % y;
    16         //printf("r  = %d 
    ", r);
    17         if(vis[r]) {
    18             //printf("return %d = %d 
    ", 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
    ", 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
    ", 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
    ", 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
    ", 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
    ", 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
    ", ans);
    94     }*/
    95     return 0;
    96 }
    40分代码
  • 相关阅读:
    JAVA---File递归遍历文件目录,输出绝对路径
    JAVA--Map集合
    (笔记)JAVA--集合实现斗地主洗牌、发牌、看牌(利用TreeSet排序)
    SVN简单使用
    System.Object
    动态创建控件
    select 查询
    数据类型
    入门(值得注意的地方)
    错误调式 异常处理
  • 原文地址:https://www.cnblogs.com/huyufeifei/p/10760649.html
Copyright © 2011-2022 走看看