莫比乌斯反演入门

定理

  • d|nμ(d),n=11n>10

    F(i)=d|if(d)
    形式一

    证明: f(n)=d|nμ(d)k|ndf(k)=d|nf(d)k|ndμ(k)
    k|ndμ(k)1=ndn=d10
    所以 f(n)=d|nμ(d)F(nd)

    还有第二种形式

  • n=Πpi|npaii
    若m有 ai>1 μ(i)=0
    否则
    ai=1 有奇数个则 μ(i)=1
    ai=1 有偶数个则 μ(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|df(d) 显然 F(i)gcd(x,y)==i 的倍数的有序数对
又显然 F(i)=(b/k/i)(d/k/i)
然后用莫比乌斯反演求出 f(i)
f(1)=1|dμ(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)) 优化去算就好了 预处理 μ(i) 的前缀和)
那么我们可以把连续的一段d一起来算(分块):
a/d=x ,那么最后一个 a/d=xd=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;
}

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值