莫比乌斯反演入门

定理

  • \(\sum_{d|n}\mu(d), n=1时为1,n > 1时为0\)

  • \(F(i)=\sum_{d|i}f(d)\)
    形式一

    证明:\(f(n)=\sum_{d|n}\mu(d)\sum_{k|\frac{n}{d}}f(k)=\sum_{d|n}f(d)\sum_{k|\frac{n}{d}}\mu(k)\)
    \(\sum_{k|\frac{n}{d}}\mu(k)当1=\frac{n}{d},即n=d时才为1,否则为0\)
    所以$f(n)=\sum_{d|n}\mu(d)F(\frac {n}{d}) $

还有第二种形式

  • $令 n = \Pi_{p_i|n} p_{i} ^ {a_{i}} \( 若m有\)a_i>1\(则\)\mu(i) = 0$
    否则
    \(a_i=1\)有奇数个则\(\mu(i) = -1\)
    \(a_i=1\)有偶数个则\(\mu(i) = 1\)
    不会证明找规律可证
    有些大佬称之为容斥系数

求解u的代码,筛素数法

IL void Prime(){
    isprime[1] = mu[1] = 1;
    for(RG int i = 2; i < _; i++){
        if(!isprime[i]) prime[++cnt] = i, mu[i] = -1;
        for(RG int j = 1; j <= cnt && i * prime[j] < _; j++){
            isprime[prime[j] * i] = 1;
            if(!(i % prime[j])){  mu[i * prime[j]] = 0; break;  }
            else mu[prime[j] * i] = -mu[i];
        }
    }
}

应用

1

【HDU1695】GCD
求a<=x<=b,c<=y<=d,a<=x<=b,c<=y<=d
且gcd(x,y)=k的无序数对的个数
其中,你可以假定a=c=1a=c=1
所有数都<=100000<=100000
数据组数<=3000



即求\(gcd(x, y)==1, x<=b/k, y<=d/k\)的无序数对个数

设$f(i)为gcd(x, y) == i \(的有序数对 \)F(i) = ∑i|d f(d) \(显然\)F(i)表示gcd(x, y)==i$的倍数的有序数对
又显然 $F(i) = (b/k/i)*(d/k/i) $
然后用莫比乌斯反演求出 \(f(i)\)
$f(1)=∑_{1|d} \mu(d)F(d) $
因为是无序的,而我们求的是有序的,所以要去重

# include <bits/stdc++.h>
# define IL inline
# define RG register
# define Fill(a, b) memset(a, b, sizeof(a))
using namespace std;
typedef long long ll;
const int _(100010);

IL ll Read(){
    RG char c = getchar(); RG ll x = 0, z = 1;
    for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
    for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + c - '0';
    return x * z;
}

int mu[_], prime[_], isprime[_], cnt;

IL void Prime(){
    isprime[1] = mu[1] = 1;
    for(RG int i = 2; i < _; i++){
        if(!isprime[i]) prime[++cnt] = i, mu[i] = -1;
        for(RG int j = 1; j <= cnt && i * prime[j] < _; j++){
            isprime[prime[j] * i] = 1;
            if(!(i % prime[j])){  mu[i * prime[j]] = 0; break;  }
            else mu[prime[j] * i] = -mu[i];
        }
    }
}

IL ll Calc(RG ll x, RG ll y){
    RG ll ans = 0, _ans = 0, z = min(x, y);
    for(RG ll i = 1; i <= z; i++) ans += mu[i] * (x / i) * (y / i);
    for(RG ll i = 1; i <= z; i++) _ans += mu[i] * (z / i) * (z / i);
    return ans - _ans / 2;
}

int main(RG int argc, RG char* argv[]){
    Prime();
    RG int n = Read(), x, y, d, Case = 0;
    while(n--){
        Read(), x = Read(), Read(), y = Read(), d = Read();
        printf("Case %d: %lld\n", ++Case, d ? Calc(x / d, y / d) : 0);
    }
    return 0;
}

2

[POI2007]ZAP-Queries
对于给定的整数a,b和d,有多少正整数对x,y,满足x<=a,y<=b,并且gcd(x,y)=d
(1≤d≤a,b≤50 000) (1≤n≤50 000)



和上面的题目是一样的,只是这个是有序的,数据变大了直接蒯会TLE
因为\(n/d\)的个数只有根号个,所以,在某一段区间里面\(n/i\)是一样的,这时候我们用\((n/(n/i))\)优化去算就好了 预处理\(\mu(i)\)的前缀和)
那么我们可以把连续的一段d一起来算(分块):
\(a/d=x\),那么最后一个\(a/d=x即d=a/x\),所以这段连续的区间就是\([d,a/(a/d)]\)

# include <bits/stdc++.h>
# define IL inline
# define RG register
# define Fill(a, b) memset(a, b, sizeof(a))
using namespace std;
typedef long long ll;
const int _(100010);

IL ll Read(){
    RG char c = getchar(); RG ll x = 0, z = 1;
    for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
    for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + c - '0';
    return x * z;
}

int mu[_], prime[_], isprime[_], cnt;
ll sum[_];

IL void Prime(){
    isprime[1] = sum[1] = mu[1] = 1;
    for(RG int i = 2; i < _; i++){
        if(!isprime[i]) prime[++cnt] = i, mu[i] = -1;
        for(RG int j = 1; j <= cnt && i * prime[j] < _; j++){
            isprime[prime[j] * i] = 1;
            if(!(i % prime[j])){  mu[i * prime[j]] = 0; break;  }
            else mu[prime[j] * i] = -mu[i];
        }
    }
    for(RG int i = 2; i < _; i++) sum[i] = sum[i - 1] + mu[i];
}

IL ll Calc(RG ll x, RG ll y){
    RG ll ans = 0, z = min(x, y);
    for(RG ll i = 1, j; i <= z; i = j + 1){
        j = min(x / (x / i), y / (y / i));
        ans += (sum[j] - sum[i - 1]) * (x / i) * (y / i);
    }
    return ans;
}

int main(RG int argc, RG char* argv[]){
    Prime();
    RG int n = Read(), x, y, d;
    while(n--){
        x = Read(), y = Read(), d = Read();
        printf("%lld\n", d ? Calc(x / d, y / d) : 0);
    }
    return 0;
}

3

[HAOI2011]Problem b
对于给出的n个询问,每次求有多少个数对(x,y),满足a≤x≤b,c≤y≤d,且gcd(x,y) = k,gcd(x,y)函数为x和y的最大公约数。
1≤n≤50000,1≤a≤b≤50000,1≤c≤d≤50000,1≤k≤50000



一样的再加上一个容斥即可

# include <bits/stdc++.h>
# define IL inline
# define RG register
# define Fill(a, b) memset(a, b, sizeof(a))
using namespace std;
typedef long long ll;
const int _(100010);

IL ll Read(){
    RG char c = getchar(); RG ll x = 0, z = 1;
    for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
    for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + c - '0';
    return x * z;
}

int mu[_], prime[_], isprime[_], cnt;
ll sum[_];

IL void Prime(){
    isprime[1] = sum[1] = mu[1] = 1;
    for(RG int i = 2; i < _; i++){
        if(!isprime[i]) prime[++cnt] = i, mu[i] = -1;
        for(RG int j = 1; j <= cnt && i * prime[j] < _; j++){
            isprime[prime[j] * i] = 1;
            if(!(i % prime[j])){  mu[i * prime[j]] = 0; break;  }
            else mu[prime[j] * i] = -mu[i];
        }
    }
    for(RG int i = 2; i < _; i++) sum[i] = sum[i - 1] + mu[i];
}

IL ll Calc(RG ll x, RG ll y){
    RG ll ans = 0, z = min(x, y);
    for(RG ll i = 1, j; i <= z; i = j + 1){
        j = min(x / (x / i), y / (y / i));
        ans += (sum[j] - sum[i - 1]) * (x / i) * (y / i);
    }
    return ans;
}

int main(RG int argc, RG char* argv[]){
    Prime();
    RG int n = Read(), a, b, c, d, k;
    while(n--){
        a = Read(), b = Read(), c = Read(), d = Read(), k = Read();
        printf("%lld\n", Calc(b / k, d / k) - Calc((a - 1) / k, d / k) - Calc((c - 1) / k, b / k) + Calc((a - 1) / k, (c - 1) / k));
    }
    return 0;
}

以上就是本蒟蒻的一些见解
题目可能比较水

posted @ 2018-01-09 10:09  Cyhlnj  阅读(323)  评论(0编辑  收藏  举报