zoukankan      html  css  js  c++  java
  • YY的GCD 莫比乌斯反演

    ~~~题面~~~

    题解:

    $ans = sum_{x = 1}^{n}sum_{y = 1}^{m}sum_{i = 1}^{k}[gcd(x, y) == p_{i}]$其中k为质数个数
        $$ans = sum_{i = 1}^{k}sum_{x = 1}^{n}sum_{y = 1}^{m}[gcd(x, y) == p_{i}]$$
        设$f(d)$表示$x$从$1$到$n$,$y$从$1$到$m$,$gcd == d$的个数,$g(d)$表示相同条件下$d | gcd$(即$gcd$为$d$的倍数)的个数
        那么$$f(d) = sum_{x = 1}^{n}sum_{y = 1}^{m}[gcd(x, y) == d]$$,$$g(d) = lfloor{frac{n}{d}} floorlfloor{frac{m}{d}} floor$$
        因为$$g(x) = sum_{x|d}^{min(n, m)}f(d)$$
        所以反演一下。
        $$f(x) = sum_{x | d}^{min(n, m)}mu(frac{d}{x})g(d)$$
        那么$ans = sum_{i = 1}^{k}f(p_{i})$
        $$= sum_{i = 1}^{k}sum_{x | d}^{min(n, m)}mu(frac{d}{x})g(d)$$
        改成直接枚举系数
        $$= sum_{i = 1}^{k}sum_{d = 1}^{lfloor{frac{min(n, m)}{p_{i}}} floor}mu(d)g(dp_{i})$$
        $$= sum_{i = 1}^{k}sum_{d = 1}^{lfloor{frac{min(n, m)}{p_{i}}} floor}mu(d)lfloor{frac{n}{dp_{i}} floor lfloor{frac{m}{dp_{i}} floor}}$$<---枚举每个$mu(d)分别被每个质数统计了几次$
        $$= sum_{T = 1}^{min(n, m)} lfloor{frac{n}{T}} floor lfloor{frac{m}{T}} floorsum_{k|T}{mu(frac{T}{k})}$$<---枚举每个$lfloor{frac{n}{T}} floor lfloor{frac{m}{T}} floor$会给哪些$mu$做贡献(哪些$mu$会在某次被统计$lfloor{frac{n}{T}} floor lfloor{frac{m}{T}} floor$次)
        然后暴力枚举质数和系数,给对应的$mu$做贡献(质数$p_{i}$给它的倍数做贡献),统计前缀和,对前面的$lfloor{frac{n}{T}} floor lfloor{frac{m}{T}} floor$进行整数分块处理

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define R register int
     4 #define AC 10000100
     5 #define LL long long 
     6 int n, m, tot, t;
     7 int prime[AC], mu[AC];
     8 LL s[AC], ans;
     9 bool z[AC];
    10 
    11 inline int read()
    12 {
    13     int x = 0;char c = getchar();
    14     while(c > '9' || c < '0') c = getchar();
    15     while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    16     return x;
    17 }
    18 
    19 void pre()
    20 {
    21     int now;
    22     mu[1] = 1;
    23     for(R i = 2; i <= 10000000; i++)
    24     {
    25         if(!z[i]) prime[++tot] = i, mu[i] = -1;
    26         for(R j = 1; j <= tot; j++)
    27         {
    28             now = prime[j];
    29             if(i * now > 10000000) break;
    30             z[i * now] = true;
    31             if(!(i % now)) break;            
    32             mu[now * i] = -mu[i];
    33         }
    34     }
    35     int p;
    36     for(R i = 1; i <= tot; i++)//枚举质数 
    37     {
    38         p = prime[i];//卡常
    39         for(R j = p; j <= 10000000; j += p) //枚举倍数
    40             s[j] += mu[j / p];//or j = 系数, s[j * prime[i]] += mu[j];
    41     }
    42     for(R i = 1; i <= 10000000; i++) s[i] += s[i - 1];
    43 }
    44 
    45 void work()
    46 {
    47     t = read();
    48     while(t--)
    49     {
    50         int pos = 0;
    51         ans = 0;
    52         n = read(), m = read();
    53         int b = min(n, m);//这里要取min!!!
    54         for(R i = 1; i <= b; i = pos + 1)
    55         {
    56             pos = min(n / (n / i), m / (m / i));
    57             ans += (LL) (n / i) * (LL) (m / i) * (LL) (s[pos] - s[i - 1]);//error 只有ans是LL是不够的
    58         }        
    59         printf("%lld
    ", ans);
    60     }
    61 }
    62 
    63 int main()
    64 {
    65 //    freopen("in.in", "r", stdin);
    66     //freopen("YYnoGCD.in", "r", stdin);
    67     //freopen("YYnoGCD.out", "w", stdout);
    68     pre();
    69     work();
    70     //fclose(stdin);
    71     //fclose(stdout);
    72     return 0;
    73 }
  • 相关阅读:
    Chrome开发者工具中Elements(元素)断点的用途
    最简单的SAP云平台开发教程
    Java实现 LeetCode 495 提莫攻击
    Java实现 LeetCode 494 目标和
    Java实现 LeetCode 494 目标和
    Java实现 LeetCode 494 目标和
    Java实现 LeetCode 493 翻转对
    Java实现 LeetCode 493 翻转对
    Java实现 LeetCode 493 翻转对
    Java实现 LeetCode 492 构造矩形
  • 原文地址:https://www.cnblogs.com/ww3113306/p/9486079.html
Copyright © 2011-2022 走看看