线性筛、积性函数、Mobius反演



前言

不得不说这次意外地因为要啃Mobius反演而对以前学习的欧拉筛有了更深刻的理解
不过杜教筛只能等到下次学习了再更新了 qwq
因为思路是所有题目写完后才来重新推导的,所以会有一些划掉的地方,望各位dalao谅解 qwq

  1. 莫比乌斯反演
    http://codeforces.com/blog/entry/53925
    https://blog.sengxian.com/algorithms/mobius-inversion-formula
  2. 线性筛与积性函数
    https://wenku.baidu.com/view/2d706761aa00b52acec7ca63.html?re=view

Gcd - HYSBZ - 2818


思路

  #include <iostream>
  #include <cstdio>
  #include <list>
  #include <cstring>
  #include <algorithm>
  #include <functional>
  using namespace std;
  const int N = 1e7 + 5;
  const int inf = 0x3f3f3f3f;
  typedef long long ll;

  ll mu[N], sum[N];
  bool isprime[N];
  int prime[N], tot;

  void init(){
      memset(isprime, true, sizeof(isprime));
      tot = 0, mu[1] = 1, sum[1] = 1;
      for(int i = 2; i < N; i++){
          if(isprime[i]){
              prime[tot++] = i;
              mu[i] = -1;
          }
          for(int j = 0; j < tot && i * prime[j] < N; j++){
              isprime[i * prime[j]] = false;
              if(i % prime[j] == 0){
                  mu[i * prime[j]] = 0;
                  break;
              }else{
                  mu[i * prime[j]] = -mu[i];
              }
          }
          sum[i] = sum[i - 1] + mu[i];
      }
  }

  ll solve(ll n){
      ll ans = 0;
      for(int j = 0; j < tot && prime[j] <= n; j++){
          ll p = prime[j], nn = n/p;
          for(int k = 1, last = 1; k <= nn; k = last + 1){
              last = nn / (nn/k);
              ans += (nn/k) * (nn/k) * (sum[last] - sum[k - 1]);
          }
      }
      return ans;
  }

  int main(){
      init();
      int n;
      while(~scanf("%d", &n)){
          printf("%lld\n", solve(n));
      }
  }


Sky - Code POJ - 3904


题意
给定N个数的数组ai(1 <= N <= 10000, 1 <= ai <= 10000),问从中选择4个数使其gcd为1的方案数
思路

  #include <iostream>
  #include <cstdio>
  #include <list>
  #include <cstring>
  #include <algorithm>
  #include <functional>
  using namespace std;
  const int N = 1e4 + 5;
  const int inf = 0x3f3f3f3f;
  typedef long long ll;

  int mu[N];
  int prime[N], tot;
  bool isprime[N];
  int num[N];

  void init(){
      memset(isprime, true, sizeof(isprime));
      mu[1] = 1, tot = 0;
      for(int i = 2; i < N; i++){
          if(isprime[i]){
              prime[tot++] = i;
              mu[i] = -1;
          }
          for(int j = 0; j < tot && i * prime[j] < N; j++){
              isprime[i * prime[j]] = false;
              if(i % prime[j] == 0){
                  mu[i * prime[j]] = 0;
                  break;
              }else{
                  mu[i * prime[j]] = -mu[i];
              }
          }
      }
  }

  void sep(int x){
      for(int i = 1; i*i <= x; i++){
          if(x%i)     continue;
          num[i]++;
          if(i*i != x)    num[x/i]++;
      }
  }

  int main(){
      init();
      int n;
      while(~scanf("%d", &n)){
          memset(num, 0, sizeof(num));
          for(int i = 1; i <= n; i++){
              int tmp;
              scanf("%d", &tmp);
              sep(tmp);
          }

          ll sum = 0;
          for(int i = 1; i < N; i++){
              if(!mu[i] || num[i] < 4)    continue;
              sum += (ll)mu[i] * num[i] * (num[i] - 1) * (num[i] - 2) * (num[i] - 3) / 24;
          }
          printf("%lld\n", sum);
      }

  }


Simple Sum - CodeChef - SMPLSUM


题意
多组询问求 sum(n/gcd(i, n))(i = 1 -> n) 的值
思路

  #include <iostream>
  #include <cstdio>
  #include <list>
  #include <cstring>
  #include <algorithm>
  #include <functional>
  using namespace std;
  const int N = 1e7 + 5;
  const int inf = 0x3f3f3f3f;
  typedef long long ll;

  ll f[N];
  bool isprime[N];
  int prime[N], tot;
  int cnt[N];

  int quickPow(int a, int b){
      int ans = 1, base = a;
      while(b){
          if(b&1)     ans = ans * base;
          base = base * base;
          b >>= 1;
      }
      return ans;
  }

  void init(){
      memset(isprime, true, sizeof(isprime));
      tot = 0, f[1] = 1;
      for(int i = 2; i < N; i++){
          if(isprime[i]){
              prime[tot++] = i;
              cnt[i] = 1;
          }
          for(int j = 0; j < tot && (ll)i * prime[j] < N; j++){
              isprime[i * prime[j]] = false;
              if(i % prime[j] == 0){
                  cnt[i * prime[j]] = cnt[i] + 1;
                  break;
              }else{
                  cnt[i * prime[j]] = 1;
              }
          }
      }

      for(int j = 0; j < tot; j++){
          f[prime[j]] = (ll)prime[j] * (prime[j] - 1) + 1;
          for(ll pk = prime[j], k = 1; pk * prime[j] < N; pk *= prime[j], k++){
              f[pk * prime[j]] = f[pk] + (pk * prime[j]) * (pk * prime[j] - pk);
          }
      }

      for(int i = 2; i < N; i++){
          for(int j = 0; j < tot && (ll)i * prime[j] < N; j++){
              if(i % prime[j] == 0){
                  int pk = quickPow(prime[j], cnt[i * prime[j]]);
                  f[i * prime[j]] = f[pk] * f[i * prime[j] / pk];
                  break;
              }else{
                  f[i * prime[j]] = f[i] * f[prime[j]];
              }
          }
      }
  }

  int main(){
      init();
      int t;
      scanf("%d", &t);
      while(t--){
          int n;
          scanf("%d", &n);
          printf("%lld\n", f[n]);
      }
  }


Sum - ACM-ICPC 2018 南京赛区网络预赛


题意
定义一个数为square-free是该数无因数是平方数,定义f(i)为一个数i分解为两个数(a,b)且a和b都不是平方数的分解方案,这里(a,b)和(b,a)视为两种方案,求sum(f(i))
思路
可以知道f(i)是一个积性函数(可以打表观察出)
那么最后要解决的就是如何计算f(p^k)的问题
当k = 1时,f(p^k) = 2
当k = 2时,f(p^k) = 1
当k >= 3时,f(p^k) = 0
下面的代码差点TLE = =

  #include <iostream>
  #include <cstdio>
  #include <list>
  #include <cstring>
  #include <algorithm>
  #include <functional>
  using namespace std;
  const int N = 2e7 + 5;
  const int inf = 0x3f3f3f3f;
  typedef long long ll;

  ll f[N], sum[N];
  bool isprime[N];
  int  prime[N], tot;
  int  cnt[N];

  int quickPow(int a, int b){
      int ans = 1, base = a;
      while(b){
          if(b&1)     ans = ans * base;
          base = base * base;
          b >>= 1;
      }
      return ans;
  }

  void init(){
      memset(isprime, true, sizeof(isprime));
      tot = 0, f[1] = 1, sum[1] = 1;
      for(int i = 2; i < N; i++){
          if(isprime[i]){
              prime[tot++] = i;
              cnt[i]++;
          }
          for(int j = 0; j < tot && i * prime[j] < N; j++){
              isprime[i * prime[j]] = false;
              if(i % prime[j] == 0){
                  cnt[i * prime[j]] = cnt[i] + 1;
                  break;
              }else{

                  cnt[i * prime[j]] = 1;
              }
          }
      }

      for(int i = 0; i < tot && (ll)prime[i] * prime[i] < N; i++){
          f[prime[i] * prime[i]] = 1;
          ll cur = (ll)prime[i] * prime[i] * prime[i];
          while(cur < N){
              f[cur] = 0;
              cur = cur * prime[i];
          }
      }

      for(int i = 2; i < N; i++){
          if(isprime[i]){
              f[i] = 2;
          }
          for(int j = 0; j < tot && i * prime[j] < N; j++){
              if(i % prime[j] == 0){
                  int pk = quickPow(prime[j], cnt[i * prime[j]]);
                  f[i * prime[j]] = f[pk] * f[i * prime[j] / pk];
                  break;
              }else{
                  f[i * prime[j]] = f[i] * f[prime[j]];
              }
          }
          sum[i] = f[i] + sum[i - 1];
      }
  }

  int main(){
      init();
      int t;
      scanf("%d", &t);
      while(t--){
          int n;
          scanf("%d", &n);
          printf("%lld\n", sum[n]);
      }
  }