zoukankan      html  css  js  c++  java
  • ACM-ICPC (10/11)

    莫比乌斯

    今年的多校比赛,莫比乌斯反演的题目经常出现,但是我们队对于这种题可以说是直接放掉,不是因为没学过,多少了解一些,但是也只是皮毛,导致根本就做不出来,其实想一想,其实次数多了,就可以看出原因了,没有过总结,没有过思考,昨天心血来潮,好好的看了一下莫比乌斯反演,有一点感悟!

    • 什么是莫比乌斯反演

    ...

    可以通过​ 反向推导出

    ...

    公式:

    其中 即为莫比乌斯函数。

    也可以如下定义:

    • 分解质因数,无不相同,则

    • 其他为0

    性质:积性函数

    莫比乌斯(容斥的优化)——我的理解

    怎么求莫比乌斯函数?

    • 类似于筛素数的方案,O(nlogn),一般足够了。

    • 级别,无法预处理出1~n的莫比乌斯表,利用因式分解,讨论因子的拿法(类似压状),O(sqrt(n))

    栗子:

    //求莫比乌斯函数 O(nlogn)
    void getMu() {
       for(int i = 1; i < maxn; i++) {
           int target = i == 1 ? 1 : 0;
           int delta = target - mu[i];
           mu[i] = delta;

           for(int j = i + i ; j < maxn; j+=i)
               mu[j] +=delta;
      }
    }

    // n 的 约数的莫比乌斯函数值map形式返回 O(sqrt(n))
    map<int,int> moebius(int n) {
       map<int,int> res;
       vector<int> primes;

       //枚举n的质数
       for(int i = 2; i*i <= n; i++) {
           if(n%i==0) {
               primes.push_back(i);
               while(n%i==0) n/=i;
          }
      }
       if(n!=1) primes.push_back(n);

       int m = primes.size();
       //不超过n的约数个数 次
       for(int i = 0; i < (1<<m); i++) {
           int mu = 1,d = 1;
           for(int j = 0; j < m; j++) {
               if(i>>j&1) {
                   mu*=-1;
                   d*=primes[j];
              }
          }
           res[d] = mu;
      }
       return res;
    }

    例题一:

    分析:总共26^n,根据容斥,枚举最短循环节d,减去26^d,根据莫比乌斯函数来容斥优化。注意数据范围极大!


    #include <bits/stdc++.h>

    using namespace std;

    typedef long long ll;

    // n 的 约数的莫比乌斯函数值map形式返回 O(sqrt(n))
    map<int,int> moebius(int n) {
       map<int,int> res;
       vector<int> primes;

       //枚举n的质数
       for(int i = 2; i*i <= n; i++) {
           if(n%i==0) {
               primes.push_back(i);
               while(n%i==0) n/=i;
          }
      }
       if(n!=1) primes.push_back(n);

       int m = primes.size();
       //不超过n的约数个数 次
       for(int i = 0; i < (1<<m); i++) {
           int mu = 1,d = 1;
           for(int j = 0; j < m; j++) {
               if(i>>j&1) {
                   mu*=-1;
                   d*=primes[j];
              }
          }
           res[d] = mu;
      }
       return res;
    }

    const int MOD = 10009;

    int n;

    //快速幂取模 a^b % mod;
    ll pow_mod(ll a,ll b) {
       if(b==0) return 1%MOD;
       int temp = pow_mod(a,b>>1);

       temp = temp*temp%MOD;    if(b&1) temp = (ll)temp*a%MOD;    return temp;}​void solve() {    ll res = 0;    map<int,int> mu = moebius(n);    for(auto it = mu.begin(); it != mu.end(); it++) {        //printf("%d %d ",it->first,it->second);        res += (ll)it->second*pow_mod(26,n/it->first);        res = (res%MOD + MOD) % MOD;   }    printf("%I64d ",res);}​int main(){    scanf("%d",&n);    solve();    return 0;}

    例题二:

    分析:

    注意:(1,3),(3,1)相同。

    先考虑不同的情况:

    即:两个区间内互质的对数,同理:总共 对,减去不符合情况的对数,枚举gcd,就有 * 对,然后用莫比乌斯来容斥。

    怎么解决(1,3)(3,1)相同的情况呢?

    还是容斥!!!

    减去较小区间做一遍上述操作的一半。


    #include <bits/stdc++.h>

    using namespace std;

    const int maxn = 1<<20;
    int mu[maxn];


    //求莫比乌斯函数
    void getMu() {
       for(int i = 1; i < maxn; i++) {
           int target = i == 1 ? 1 : 0;
           int delta = target - mu[i];
           mu[i] = delta;

           for(int j = i + i ; j < maxn; j+=i)
               mu[j] +=delta;
      }
    }

    int main()
    {
       getMu();

       int T;
       scanf("%d",&T);
       for(int z = 1; z <= T; z++) {
           int a,b,c,d,k;
           scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
           if(k==0) {
               puts("0");
               continue;
          }
           b/=k;
           d/=k;
           if(b>d) swap(b,d);  //b小
           long long ans1 = 0;
           for(int i = 1; i <= b; i++)
               ans1 += (long long)mu[i]*(b/i)*(d/i);

           long long ans2 = 0;
           for(int i = 1; i <= b; i++)            ans2 += (long long)mu[i]*(b/i)*(b/i);        ans1 -= ans2/2;        printf("%I64d ",ans1);   }​​    return 0;}

    例题三:

    题意:给定一个正方体的区间,从坐标(0,0,0)处可以看到多少个整点?

    分析:(1,1,1) 能看到,但是(2,2,2)就三点共线挡住了,同理(2,3,4)能看到,但是(4,6,8)就看不到了,可以看出就是 的点,被除以gcd的点挡住了。也就是求 (x,y,z)互质的点对,就是上一题加一维。

    值得注意的是区间是[1,n],整个坐标系可以x = 0等,x等于0,相当于一个平面,减一围。

    还有3个坐标轴上的点。


    #include <bits/stdc++.h>

    using namespace std;

    const int maxn = 1<<20;
    int mu[maxn];


    //求莫比乌斯函数
    void getMu() {
       for(int i = 1; i < maxn; i++) {
           int target = i == 1 ? 1 : 0;
           int delta = target - mu[i];
           mu[i] = delta;

           for(int j = i + i ; j < maxn; j+=i)
               mu[j] +=delta;
      }
    }

    typedef long long ll;

    int main()
    {
       getMu();

       int t;
       scanf("%d",&t);
       while(t--) {
           int n;
           scanf("%d",&n);

           ll ans = 3;
           for(int i = 1; i <= n; i++)
               ans+= (ll)mu[i]*(n/i)*(n/i)*(n/i+3);
           printf("%lld ",ans);


      }
       return 0;
    }

    到此,你就已经入门莫比乌斯了~~~

    TreeDream

  • 相关阅读:
    PsySH——PHP交互式控制台
    CentOS 6.5升级Python和安装IPython
    yii2 邮件发送
    Centos 6.5安装最新版谷歌浏览器-Chrome
    centos 6.5 设置屏幕保护
    PHP实现生成唯一编号(36进制的不重复编号)
    十位用户唯一ID生成策略
    0基础学java_for循环
    0基础学java_while循环
    0基础学java_逻辑变量 逻辑表达式 和条件句
  • 原文地址:https://www.cnblogs.com/TreeDream/p/7653242.html
Copyright © 2011-2022 走看看