莫比乌斯反演[学习笔记]

先咕着

\[\large \sum_{i=1}^{n} \sum_{j=1}^{m}[gcd(i,j)](n<m) \]

显然

\[\large\sum_{d|n}\varphi(d)=n \]

所以

\[\large\sum_{i=1}^{n}\sum_{j=1}^{m}\sum_{d|gcd(i,j)}\varphi(d) \]

\[\large=\sum_{d=1}^{n}\varphi(d)*\lfloor\frac{n}{d}\rfloor*\lfloor\frac{m}{d}\rfloor \]

代码大概长这样(?
预处理

   void init(int x) {
    vis[1] = 1 ; mul[1] = 1 ;
    for(int i = 2 ; i <= x ; i ++) {
      if(! vis[i]) {
        mul[i] = -1 ;
        prm[++ cnt] = i ;
      }
      for(int j = 1 ; j <= cnt && i * prm[j] <= x ; j ++) {
        int k = i * prm[j] ;
        vis[k] = true ;
        if(! (i % prm[j])) { break ; }
        else mul[k] = -mul[i] ;
      }
    }
    for(int i = 1 ; i <= x ; i ++) { pre[i] = pre[i - 1] + mul[i] ; }
  }
for(int l = 1 , r = 0 ; l <= min(n , m) ; l = r + 1) {
   r = min(n / (n / l) , m / (m / l)) ;
   ans += 1ll * (n / l) * (m / l) * (sum[r] - sum[l - 1]) ;
}

[POI2007]ZAP-Queries

\(\large \sum_{i=1}^{n}\sum_{i=1}^{m}[gcd(i,j)==d]\)

显然刚开始的套路是

\[\large \sum_{i=1}^{\lfloor \frac{n}{d}\rfloor}\sum_{i=1}^{\lfloor \frac{m}{d} \rfloor}[gcd(i,j)==1] \]

然后直接对 \(\lfloor \frac{n}{d} \rfloor\) \(\lfloor \frac{m}{d} \rfloor\) 做一个 \(\sum_{i=1}^{n}\sum_{j=1}^{m}[gcd(i,j)==1]\)
普通的数论分块即可…

#include <bits/stdc++.h>

#define ls(x) ch[x][0]
#define rs(x) ch[x][1]
#define rep(i , j , k) for(int i = j ; i <= k ; i ++)
#define Rep(i , j , k) for(int i = j ; i >= k ; i --)
#define int long long
using namespace std ;
using ll = long long ;
using pii = pair <int , int> ;
using vii = vector <int> ;

auto ot = [&]() { cerr << "ATS TXDY" << '\n' ; int ATS_nantf_txdy = true ; } ;
auto _ios = [&]() { ios :: sync_with_stdio(false) ; cin.tie(nullptr) ; cout.tie(nullptr) ; } ;

namespace stO_ATS_Orz {
  template < class T > void cmax(T & x , T y) { if(x > y) x = y ; }
  template < class T > void cmin(T & x , T y) { if(x < y) x = y ; }
  template < class T > void abs(T x) { if(x < 0) x = -x ; }
  const int N = 5e4 + 10 ;
  int t , n , m , k ;
  int prm[N] , mul[N] , pre[N] , cnt = 0 ;
  bool vis[N] ;
  void init(int x) {
    vis[1] = 1 ; mul[1] = 1 ;
    for(int i = 2 ; i <= x ; i ++) {
      if(! vis[i]) {
        mul[i] = -1 ;
        prm[++ cnt] = i ;
      }
      for(int j = 1 ; j <= cnt && i * prm[j] <= x ; j ++) {
        int k = i * prm[j] ;
        vis[k] = true ;
        if(! (i % prm[j])) { break ; }
        else mul[k] = -mul[i] ;
      }
    }
    for(int i = 1 ; i <= x ; i ++) { pre[i] = pre[i - 1] + mul[i] ; }
  }
  void main() {
    init(5e4) ;
    cin >> t ;
    while(t --) {
      cin >> n >> m >> k ;
      n /= k ;
      m /= k ;
      int ans = 0 ;
      for(int l = 1 , r = 0 ; l <= min(n , m) ; l = r + 1) {
        r = min(n / (n / l) , m / (m / l)) ;
        ans += (n / l) * (m / l) * (pre[r] - pre[l - 1]) ;
      }
      cout << ans << '\n' ;
    }
  }
}
signed main() {
  _ios() ; ot() ;
  return stO_ATS_Orz :: main() , 0 ;
}

YY的GCD

\(\large \sum_{i=1}^{n}\sum_{j=1}^{m}[gcd(i,j)\in prime]\)
prime 为 质数

这样每次就不能单纯的使用前缀和\(sum\)直接加上\(\mu(i)\)
这样就考虑让 \(x \in prime\) 的倍数加上 \(\mu(i)\) 然后计算前缀和…

\[\large=\sum_{d=1}^{n}\varphi(d)*\lfloor\frac{n}{d}\rfloor*\lfloor\frac{m}{d}\rfloor (d \in prime) \]

#include <bits/stdc++.h>

#define ls(x) ch[x][0]
#define rs(x) ch[x][1]
#define rep(i , j , k) for(int i = j ; i <= k ; i ++)
#define Rep(i , j , k) for(int i = j ; i >= k ; i --)
#define int long long
using namespace std ;
using ll = long long ;
using pii = pair <int , int> ;
using vii = vector <int> ;

auto ot = [&]() { cerr << "ATS TXDY" << '\n' ; int ATS_nantf_txdy = true ; } ;
auto _ios = [&]() { ios :: sync_with_stdio(false) ; cin.tie(nullptr) ; cout.tie(nullptr) ; } ;

namespace stO_ATS_Orz {
  template < class T > void cmax(T & x , T y) { if(x > y) x = y ; }
  template < class T > void cmin(T & x , T y) { if(x < y) x = y ; }
  template < class T > void abs(T x) { if(x < 0) x = -x ; }
  const int N = 1e7 + 10 ;
  int prm[N] , g[N] , mul[N] , cnt = 0 ;
  ll sum[N] ;
  bool vis[N] ;
  void init(int n) {
    mul[1] = 1 ;
    for(int i = 2 ; i <= n ; i ++) {
      if(! vis[i]) {
        mul[i] = -1 ;
        prm[++ cnt] = i ;
      }
      for(int j = 1 ; j <= cnt && prm[j] * i <= n ; j ++) {
        vis[i * prm[j]] = 1 ;
        if(! (i % prm[j])) break ;
        else mul[i * prm[j]] = - mul[i] ;
      }
    }
    for(int j = 1 ; j <= cnt ; j ++)
      for(int i = 1 ; i * prm[j] <= n ; i ++)
        g[i * prm[j]] += mul[i] ;
    for(int i = 1 ; i <= n ; i ++) sum[i] = sum[i - 1] + g[i] ;
  }
  void main() {
    init(1e7) ;
    int t ;
    cin >> t ;
    while(t --) {
      int n , m ;
      cin >> n >> m ;
      ll ans = 0 ;
      for(int l = 1 , r = 0 ; l <= min(n , m) ; l = r + 1) {
        r = min(n / (n / l) , m / (m / l)) ;
        ans += 1ll * (n / l) * (m / l) * (sum[r] - sum[l - 1]) ;
      }
      cout << ans << '\n' ;
    }
  }
}
signed main() {
  _ios() ; ot() ;
  return stO_ATS_Orz :: main() , 0 ;
}

[HAOI2011]Problem b

与上面那题类似

\(\large\sum_{i=a}^{b}\sum_{j=c}^{d}[gcd(i,j)==d]\)

直接化成上面的形式 然后数论分块…容斥一下就可以了

\[\large \sum_{i=1}^{b}\sum_{j=1}^{d}[gcd(i,j)==1] - \sum_{i=1}^{a-1}\sum_{j=1}^{d}[gcd(i,j)==1] - \sum_{i=1}^{b}\sum_{j=1}^{c-1}[gcd(i,j)==1] + \sum_{i=1}^{a-1}\sum_{j=1}^{c-1}[gcd(i,j)==1] \]

#include <bits/stdc++.h>

#define ls(x) ch[x][0]
#define rs(x) ch[x][1]
#define rep(i , j , k) for(int i = j ; i <= k ; i ++)
#define Rep(i , j , k) for(int i = j ; i >= k ; i --)
#define int long long
using namespace std ;
using ll = long long ;
using pii = pair <int , int> ;
using vii = vector <int> ;

auto ot = [&]() { cerr << "ATS TXDY" << '\n' ; int ATS_nantf_txdy = true ; } ;
auto _ios = [&]() { ios :: sync_with_stdio(false) ; cin.tie(nullptr) ; cout.tie(nullptr) ; } ;

namespace stO_ATS_Orz {
  template < class T > void cmax(T & x , T y) { if(x > y) x = y ; }
  template < class T > void cmin(T & x , T y) { if(x < y) x = y ; }
  template < class T > void abs(T x) { if(x < 0) x = -x ; }
  const int N = 5e4 + 10 ;
  int t ;
  int prm[N] , mul[N] , pre[N] , cnt = 0 ;
  bool vis[N] ;
  void init(int x) {
    mul[1] = 1 ;
    for(int i = 2 ; i <= x ; i ++) {
      if(! vis[i]) {
        mul[i] = -1 ;
        prm[++ cnt] = i ;
      }
      for(int j = 1 ; j <= cnt && i * prm[j] <= x ; j ++) {
        vis[i * prm[j]] = 1 ;
        if(! (i % prm[j])) { break ; }
        else mul[i * prm[j]] = -mul[i] ;
      }
    }
    for(int i = 1 ; i <= x ; i ++) { pre[i] = pre[i - 1] + mul[i] ; }
  }
  int solve(int n , int m , int k) {
    n /= k ; m /= k ;
    int ans = 0 ;
    for(int l = 1 , r = 0 ; l <= min(n , m) ; l = r + 1) {
      r = min(n / (n / l) , m / (m / l)) ;
      ans += (n / l) * (m / l) * (pre[r] - pre[l - 1]) ;
    }
    return ans ;
  }
  void main() {
    init(5e4) ;
    cin >> t ;
    while(t --) {
      int a , b , c , d , k ;
      cin >> a >> b >> c >> d >> k ;
      cout << solve(b , d , k) - solve(b , c - 1 , k) - solve(a - 1 , d , k) + solve(a - 1 , c - 1 , k) << '\n' ;
    }
  }
}
signed main() {
  _ios() ; ot() ;
  return stO_ATS_Orz :: main() , 0 ;
}

[SDOI2015]约数个数和

SDOI 不是码农就是毒瘤… 真好…

这题是求 \(\large \sum_{i=1}^{n} \sum_{j=1}^{m} d(i*j)\) (d(x)表示约数个数)

刚看到是没啥思路的… 然后发现
上式其实等于

\[\large \sum_{i=1}^{n}\sum_{j=1}^{m}\sum_{x|j}\sum_{y|j}[gcd(x,y)==1] \]

\[=\large \sum_{i=1}^{n}\sum_{j=1}^{m}\lfloor\frac{n}{i}\rfloor\lfloor\frac{m}{j}\rfloor[gcd(i,j)==1] \]

\(g(x) = \sum_{i=1}^{n}\sum_{j=1}^{m}\lfloor\frac{n}{i}\rfloor\lfloor\frac{m}{j}\rfloor[x|gcd(i,j)]\)
\(g(x) = \sum_{i=1}^{n}\sum_{j=1}^{m}\lfloor\frac{n}{ix}\rfloor\lfloor\frac{m}{jx}\rfloor\)
然后按照莫反的套路来求就可以了…

#include <bits/stdc++.h>

#define ls(x) ch[x][0]
#define rs(x) ch[x][1]
#define rep(i , j , k) for(int i = j ; i <= k ; i ++)
#define Rep(i , j , k) for(int i = j ; i >= k ; i --)

using namespace std ;
using ll = long long ;
using pii = pair <int , int> ;
using vii = vector <int> ;
#define int long long
auto ot = [&]() { cerr << "ATS TXDY" << '\n' ; int ATS_nantf_txdy = true ; } ;
auto _ios = [&]() { ios :: sync_with_stdio(false) ; cin.tie(nullptr) ; cout.tie(nullptr) ; } ;

namespace stO_ATS_Orz {
  template < class T > void cmax(T & x , T y) { if(x > y) x = y ; }
  template < class T > void cmin(T & x , T y) { if(x < y) x = y ; }
  template < class T > void abs(T x) { if(x < 0) x = -x ; }
  const int N = 5e4 + 10 ;
  bool vis[N] ;
  int prm[N] , cnt = 0 , mu[N] ;
  int sum[N] , g[N] ;
  void init(int x) {
    mu[1] = 1 ;
    for(int i = 2 ; i <= x ; i ++) {
      if(! vis[i]) { prm[++ cnt] = i ; mu[i] = -1 ; }
      for(int j = 1 ; j <= cnt && i * prm[j] <= x ; j ++) {
        vis[prm[j] * i] = 1 ;
        if(! (i % prm[j])) break ;
        else mu[i * prm[j]] = - mu[i] ;
      }
    }
    rep(i , 1 , x) sum[i] = sum[i - 1] + mu[i] ;
    rep(i , 1 , x) {
      ll ans = 0 ;
      for(int l = 1 , r = 0 ; l <= i ; l = r + 1) {
        r = (i / (i / l)) ;
        ans += 1ll * (r - l + 1) * 1ll * (i / l) ;
      }
      g[i] = ans ;
    }
  }
  void main() {
    init(5e4) ;
    int t ;
    cin >> t ;
    while(t --) {
      int n , m ;
      cin >> n >> m ;
      int ans = 0 ;
      for(int l = 1 , r = 0 ; l <= min(n , m) ; l = r + 1) {
        r = min(n / (n / l) , m / (m / l)) ;
        ans += 1ll * (sum[r] - sum[l - 1]) * g[n / l] * g[m / l] ;
      }
      cout << ans << '\n' ;
    }
  }
}
signed main() {
  _ios() ; ot() ;
  return stO_ATS_Orz :: main() , 0 ;
}
posted @ 2019-12-02 10:45  _Isaunoya  阅读(192)  评论(0编辑  收藏  举报