zoukankan      html  css  js  c++  java
  • hdu1695(莫比乌斯反演模板)

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1695

    题意: 对于 a, b, c, d, k . 有 x 属于 [a, b],  y 属于 [c, d], 求 gcd(x, y) = k 的 x, y 的对数 . 其中 a = b = 1 .

    注意: (x, y), (y, x) 算一种情况 .

    思路: 莫比乌斯反演

    可以参考一下: http://blog.csdn.net/lixuepeng_001/article/details/50577932

    公式: F(n) = sigma f(d) , 其中 (n | d) ==> f(n) = sigma u(d / n) * F(d) , 其中 (n | d) .

    其中 u 为莫比乌斯函数, 定义为:

      若 d = 1, 则 u(d) = 1;

      若 d = p1p2...pk, 则 u(d) = (-1)^k , 其中 pi 为互异质数;

      其他 u(d) = 0;

    对于 gcd(x, y) = k, 显然有 gcd(x / k, y / k) = 1 . 那么原题等价于求 gcd(x, y) = 1, 其中 x 属于 (1, b / k), y 属于 (1, d / k) .

    然后定义 f(n) 表示满足条件的 gcd(x,y) = n 的 (x, y) 对数,

    再定义 F(n) 表示满足 n | gcd(x,y) 的 (x, y) 对数, 即 gcd(x, y) % n = 0 的x, y对数 .

    那么我们要求的就是 f(1) .

    通过前面推理不难发现 F(x) = n / x * m / x, 其中 n = b / k, m = d / k;

    那么 f(1) = sigma u(d / 1) * F(d), 其中 1 | d 且 d <= min(n, m);

    即 f(1) = sigma u(d) * (n / d) * (m / d);

    关于去重: f'(1) = sigma u(d) * (n / d) * (n / d), 其中 n 为 m, n中的最小值, 则 sol = f(1) - f'(1) / 2;

    代码:

     1 #include <iostream>
     2 #include <stdio.h>
     3 #include <string.h>
     4 #define ll long long
     5 using namespace std;
     6 
     7 const int MAXN = 1e6 + 10;
     8 
     9 bool check[MAXN];
    10 int mu[MAXN], prime[MAXN];
    11 
    12 void Moblus(void){
    13     memset(check, false, sizeof(check));
    14     int tot = 0;
    15     mu[1] = 1;
    16     for(int i = 2; i < MAXN; i++){
    17         if(!check[i]){
    18             prime[tot++] = i;
    19             mu[i] = -1;
    20         }
    21         for(int j = 0; j < tot && i * prime[j] < MAXN; j++){
    22             check[i * prime[j]] = true;
    23             if(i % prime[j] == 0){
    24                 mu[i * prime[j]] = 0;
    25                 break;
    26             }else mu[i * prime[j]] = -mu[i];
    27         }
    28     }
    29 }
    30 
    31 int main(void){
    32     int t, a, b, c, d, k;
    33     Moblus();
    34     scanf("%d", &t);
    35     for(int i = 1; i <= t; i++){
    36         scanf("%d%d%d%d%d", &a, &b, &c, &d, &k);
    37         if(k == 0){
    38             printf("Case %d: 0
    ", i);
    39             continue;
    40         }
    41         if(b > d) swap(b, d);
    42         b /= k;
    43         d /= k;
    44         ll ans1 = 0, ans2 = 0;
    45         for(int j = 1; j <= b; j++){
    46             ans1 += (ll)mu[j] * (b / j) * (d / j);
    47         }
    48         for(int j = 1; j <= b; j++){
    49             ans2 += (ll)mu[j] * (b / j) * (b / j);
    50         }
    51         printf("Case %d: %lld
    ",i, ans1 - (ans2 >> 1));
    52     }
    53     return 0;
    54 }
    View Code

    因为对于某些连续的 i 会有 b / i, d / i 是相同的, 这种情况可以通过前缀和优化一下 .

    优化代码:

     1 #include <iostream>
     2 #include <stdio.h>
     3 #include <string.h>
     4 #define ll long long
     5 using namespace std;
     6 
     7 const int MAXN = 1e5 + 10;
     8 
     9 bool check[MAXN];
    10 int prime[MAXN], mu[MAXN], sum[MAXN];
    11 
    12 void Moblus(void){
    13     memset(check, false, sizeof(check));
    14     int tot = 0;
    15     mu[1] = 1;
    16     sum[1] = 1;
    17     for(int i = 2; i < MAXN; i++){
    18         if(!check[i]){
    19             prime[tot++] = i;
    20             mu[i] = -1;
    21         }
    22         for(int j = 0; j < tot && prime[j] * i < MAXN; j++){
    23             check[i * prime[j]] = true;
    24             if(i % prime[j] == 0){
    25                 mu[i * prime[j]] = 0;
    26                 break;
    27             }else mu[i * prime[j]] = -mu[i];
    28         }
    29         sum[i] = sum[i - 1] + mu[i];
    30     }
    31 }
    32 
    33 ll solve(int b, int d){
    34     ll ans = 0;
    35     for(int i = 1, la = 0; i <= b; i = la + 1){
    36         la = min(b / (b / i), d / (d / i));
    37         ans += (ll)(sum[la] - sum[i - 1]) * (b / i) * (d / i);
    38     }
    39     return ans;
    40 }
    41 
    42 int main(void){
    43     Moblus();
    44     int t, a, b, c, d, k;
    45     scanf("%d", &t);
    46     for(int i = 1; i <= t; i++){
    47         scanf("%d%d%d%d%d", &a, &b, &c, &d, &k);
    48         if(k == 0){
    49             printf("Case %d: 0
    ", i);
    50             continue;
    51         }
    52         b /= k;
    53         d /= k;
    54         if(b > d) swap(b, d);
    55         ll ans1 = solve(b, d);
    56         ll ans2 = solve(b, b);
    57          printf("Case %d: %lld
    ", i, ans1 - (ans2 >> 1));
    58     }
    59     return 0;
    60 }
    View Code

    就本题而言时间从 31ms 优化到了 15 ms .

  • 相关阅读:
    spring-data-jpa 二、多对一结构、Repository
    bootstrap学习(二)页面
    bootstrap学习(一)栅格、布局
    ArrayList源码学习
    python学习-Pillow图像处理
    python学习-抓取知乎图片
    python-os创建文件夹-create_dir_if_not_exist.py
    python-shutil学习
    python-argparse批量修改后缀名-batch_file_rename.py
    python-argparse使用
  • 原文地址:https://www.cnblogs.com/geloutingyu/p/7247863.html
Copyright © 2011-2022 走看看