莫比乌斯反演专题

前段时间教练讲了莫比乌斯反演,决定发一篇博客来加深自己的理解。(众所周知我是个很懒的人,不愿意去打非常复杂的\(latex\),所以我的题解里很少有需要很复杂的推式子的题。。。)

【hdu1695】GCD

题面

hdu

题解

题意:求
\[\sum_{i=1}^a \sum_{j=1}^b [gcd(i,j)==d]\]
不难发现和这个式子是等价的
\[\sum_{i=1}^{a/d} \sum_{j=1}^{b/d} [gcd(i,j)==1]\]
为了方便之后的表达,后面的\(a\)\(b\)分别为\(a/d\)\(b/d\)
我们设
\[f(d)=\sum_{i=1}^{a} \sum_{j=1}^{b} [gcd(i,j) == d]\]
\[F(n) = \sum_{n|d}f(d)\]
此时
\[F(x)=\lfloor \frac{a}{x} \rfloor \lfloor \frac{b}{x} \rfloor\]
根据莫比乌斯反演公式
\[F(n)=\sum_{n|d}f(d)\]
\[f(n)=\sum_{n|d} \mu(\frac{d}{n})F(d)\]
那么我们的答案其实就是\(f(1)\)
这里还有\(1\)个问题。
那就是\(gcd(x,y)\)\(gcd(y,x)\)是一样的。
那我们要将总答案减去这种情况\(/2\)
(当时写这份代码的时候我还不会整除分块,所以代码里并没有...)

代码

#include <bits/stdc++.h>
  
const int maxn = 1e5 + 10;
typedef long long ll;  
  
bool vis[maxn];       
int pri[maxn], mu[maxn];   
int tot, n, m, i, j, k, T, a, b, ctot;    
  
inline void prep(int n) {
    mu[1] = 1;    
    for(int i = 2;i <= n;i++) {
        if(!vis[i]) { pri[ ++pri[0] ] = i;  mu[i] = -1; }
        for(int j = 1;j <= pri[0] && i * pri[j] <= n;j++) {
            vis[ i * pri[j] ] = 1;    
            if(i % pri[j]) 
                mu[ i * pri[j] ] = -mu[i];
            else {
                mu[ i * pri[j] ] = 0;
                break;    
            }
        }
    }
}
  
int main() {
    prep(maxn - 10); 
    scanf("%d",&T);
    while(T--) {
        scanf("%d %d %d",&a,&b,&k);  
        printf("Case %d: ",++ctot);         
        if(!k) { puts("0");  continue; }
        a /= k;  b /= k;       
        if(a > b)
            a = b;       
        ll r1 = 0, r2 = 0;
        for(int i = 1;i <= std::min(a,b);i++)
            r1 += 1ll * mu[i] * (a / i) * (b / i); 
        for(int i = 1;i <= std::min(a,b);i++)
            r2 += 1ll * mu[i] * (std::min(a,b) / i) * (std::min(a,b) / i);   
        printf("%lld\n",r1 - r2 / 2);   
    }
    return 0;
}

[HAOI2011] Problem b

题面

洛谷

题解

好像就是上面那道题加个差分...
具体看代码吧。

代码

// luogu-judger-enable-o2
#include <bits/stdc++.h>
 
const int maxn = 1e5 + 10;
typedef long long ll;  
 
bool vis[maxn];       
int pri[maxn], mu[maxn], s[maxn];   
int tot, n, m, i, j, k, T, a, b, ctot, c, d;    
 
inline void prep(int n) {
    mu[1] = 1;    
    for(int i = 2;i <= n;i++) {
        if(!vis[i]) { pri[ ++pri[0] ] = i;  mu[i] = -1; }
        for(int j = 1;j <= pri[0] && i * pri[j] <= n;j++) {
            vis[ i * pri[j] ] = 1;    
            if(i % pri[j]) 
                mu[ i * pri[j] ] = -mu[i];
            else {
                mu[ i * pri[j] ] = 0;
                break;    
            }
        }
    }
    for(int i = 1;i <= n;i++)
        s[i] = s[i - 1] + mu[i];  
}
 
inline ll get(int a,int b) {
    int c = std::min(a,b);  int res = 0;
    for(int l = 1, r;l <= c;l = r + 1) {
        r = std::min(a / (a / l),b / (b / l)); 
        res += (1ll * a / (1ll * l * k)) * (1ll * b / (1ll * r * k)) * (s[r] - s[l - 1]);      
    }
    return res;
} 
 
int main() {
    prep(maxn - 10); 
    scanf("%d",&T);
    while(T--) {
        scanf("%d %d %d %d %d",&a,&b,&c,&d,&k);
        printf("%lld\n",get(b,d) - get(b,c - 1) - get(a - 1,d) + get(a - 1,c - 1));
    }
    return 0;    
}

YY的GCD

题面

洛谷

题解

题意:
\[ \sum_{i=1}^n \sum_{j=1}^m \sum_{k} [gcd(i,j)==k \&\&k \in prime ] \]
第一道题设同样的东西:
\[f(d)=\sum_{i=1}^n \sum_{j=1}^m [gcd(i,j)==d]\]
\[F(n) = \sum_{n|d}f(d)\]
\[F(d) = \lfloor \frac{n}{d} \rfloor \lfloor \frac{m}{d} \rfloor\]
此时
\[f(n)=\sum_{n|d} \mu(\lfloor \frac{d}{n} \rfloor) F(d)\]
开始解这道题,求的就是
\[\sum_{p \in prime}f(p)\]
代入公式
\[\sum_{p \in prime} \sum_{p|d} \mu(\lfloor \frac{d}{p} \rfloor) F(d)\]
枚举\(\lfloor \frac{d}{p} \rfloor\)

\[\sum_{p \in prime} \sum_{d=1}^{min(\lfloor \frac{n}{p} \rfloor, \lfloor \frac{m}{p} \rfloor)}\mu(d)F(dp)\]
\[\sum_{p \in prime} \sum_{d=1}^{min(\lfloor \frac{n}{p} \rfloor, \lfloor \frac{m}{p} \rfloor)}\mu(d) \lfloor \frac{n}{dp} \rfloor \lfloor \frac{m}{dp} \rfloor\]
\(dp\)\(T\),然后枚举\(T\)
\[\sum_{T=1}^{min(n,m)} \sum_{t|T,t \in prime} \mu (\lfloor \frac{T}{t} \rfloor) \lfloor \frac{n}{T} \rfloor \lfloor \frac{m}{T} \rfloor\]
\[\sum_{T=1}^{min(n,m)} \lfloor \frac{n}{T} \rfloor \lfloor \frac{m}{T} \rfloor (\sum_{t|T} \mu(\lfloor \frac{T}{t} \rfloor))\]
然后就可以做了。

代码

#include <bits/stdc++.h>

const int maxn = 1e7 + 10;
typedef long long ll;  

inline void _swap(int& a,int& b) {
    a ^= b ^= a ^= b;  
}

int n, m, i, j, k, T;
int mu[maxn], f[maxn], s[maxn], pri[maxn];   
bool vis[maxn]; 

inline void sieve(int n) {
    mu[1] = 1;
    for(int i = 2;i <= n;i++) {
        if(!vis[i]) { pri[ ++pri[0] ] = i;  mu[i] = -1; }
        for(int j = 1;j <= pri[0] && i * pri[j] <= n;j++) {
            vis[ i * pri[j] ] = 1;  
            if(i % pri[j])
                mu[ i * pri[j] ] = -mu[i];
            else {
                mu[ i * pri[j] ] = 0;
                break;
            }
        }
    }
    for(int i = 1;i <= pri[0];i++)
        for(int j = 1;j * pri[i] <= n;j++)
            f[ j * pri[i] ] += mu[j];
    for(int i = 1;i <= n;i++)
        s[i] = s[i - 1] + f[i];   
}

int main() {
    sieve(maxn - 10);
    scanf("%d",&T);
    while(T--) {
        scanf("%d %d",&n,&m);
        if(n > m)
            _swap(n,m); 
        ll ans = 0;  
        for(int l = 1, r = 0;l <= n;l = r + 1) {
            r = std::min(n / (n / l),m / (m / l));
            ans += 1ll * (s[r] - s[l - 1]) * (ll)(n / l) * (ll)(m / l);  
        }
        printf("%lld\n",ans);
    }
    return 0;    
}
posted @ 2019-08-07 13:38 _connect 阅读(...) 评论(...) 编辑 收藏
Live2D