• 莫比乌斯反演入门


    定理

    • (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
    ", ++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
    ", 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
    ", 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;
    }
    

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

  • 相关阅读:
    微软职位内部推荐-Software Engineer II
    微软职位内部推荐-Senior Software Engineer
    Linux日期时间显示输出
    Redis性能优化
    Can't use Subversion command line client: svn
    redis常用性能分析命令
    Linux下配置tomcat + apr + native应对高并发
    Tomcat7并发和线程数
    mongodb常用命令小结
    Spring MVC @PathVariable被截断
  • 原文地址:https://www.cnblogs.com/cjoieryl/p/8250019.html
Copyright © 2020-2023  润新知