杜教筛模板 / p4213


前言

杜教筛是一个用来快速求积性函数前缀和的方法,其时间复杂度可达到 O ( n 2 3 ) O(n^{\frac{2}{3}}) O(n32),进而可以处理1e9的问题,其原理如下: 设 待 求 和 式 为 S ( n ) = ∑ i = 1 n f ( i ) ,   n = 1 e 9 设待求和式为 S(n)= \sum_{i=1}^{n}f(i), \space n=1e9 S(n)=i=1nf(i), n=1e9我们的工作是找一个可在 O ( 1 ) O(1) O(1)时间内查询的积性函数 g g g,那么它与待求和式有如下关系: ∑ i = 1 n ( f ∗ g ) ( i ) = ∑ i = 1 n g ( i ) S ( ⌊ n i ⌋ ) \sum_{i=1}^n(f*g)(i)=\sum_{i=1}^ng(i)S(\lfloor \frac{n}{i} \rfloor) i=1n(fg)(i)=i=1ng(i)S(in)
简证: ∑ i = 1 n ( f ∗ g ) ( i ) \sum_{i=1}^n(f*g)(i) i=1n(fg)(i) = ∑ i = 1 n ∑ d ∣ i g ( d ) f ( i d ) =\sum_{i=1}^n\sum_{d|i}g(d)f(\frac{i}{d}) =i=1ndig(d)f(di) = ∑ i = 1 n ∑ j = 1 ⌊ n i ⌋ g ( i ) f ( j ) =\sum_{i=1}^n\sum_{j=1}^{\lfloor \frac{n}{i} \rfloor}g(i)f(j) =i=1nj=1ing(i)f(j) = ∑ i = 1 n g ( i ) ∑ j = 1 ⌊ n i ⌋ f ( j ) =\sum_{i=1}^ng(i)\sum_{j=1}^{\lfloor \frac{n}{i} \rfloor}f(j) =i=1ng(i)j=1inf(j) = ∑ i = 1 n g ( i ) S ( ⌊ n i ⌋ ) =\sum_{i=1}^ng(i)S(\lfloor \frac{n}{i} \rfloor) =i=1ng(i)S(in)进而可以得到一个方便计算的递推式: g ( 1 ) S ( n ) = ∑ i = 1 n ( f ∗ g ) ( i ) − ∑ i = 2 n g ( i ) S ( ⌊ n i ⌋ ) g(1)S(n)=\sum_{i=1}^n(f*g)(i) - \sum_{i=2}^ng(i)S(\lfloor \frac{n}{i} \rfloor) g(1)S(n)=i=1n(fg)(i)i=2ng(i)S(in)
我们来分析一下它的时间复杂度,假设我们已预处理过 [ 1 , n ] [1,\sqrt{n}] [1,n ],那么 [ 1 , n ] [1,\sqrt{n}] [1,n ]内的前缀和的前缀和的查询为 O ( t ) O(\sqrt{t}) O(t ) O ( S ( n ) ) = O ( 1 ) − O ( ∫ 2 n n x d x ) = O ( n 3 4 ) O(S(n))=O(1)-O(\int_2^{\sqrt{n}}\sqrt{\frac{n}{x}}dx)=O(n^{\frac{3}{4}}) O(S(n))=O(1)O(2n xn dx)=O(n43)这个复杂度不错,但我们可以继续改进,预处理 [ 1 , n 2 3 ] [1,n^{\frac{2}{3}}] [1,n32]的前缀和,则可以得到 O ( n 2 3 ) O(n^{\frac{2}{3}}) O(n32)的复杂度,证明方法同上
下面给出几个常见的辅助函数: S ( i ) = ∑ i = 1 n μ ( i ) , g ( i ) = I ( i ) S(i)=\sum_{i=1}^{n}\mu(i), g(i)=I(i) S(i)=i=1nμ(i),g(i)=I(i) S ( i ) = ∑ i = 1 n φ ( i ) , g ( i ) = I ( i ) S(i)=\sum_{i=1}^{n}\varphi(i), g(i)=I(i) S(i)=i=1nφ(i),g(i)=I(i) S ( i ) = ∑ i = 1 n i 2 φ ( i ) , g ( i ) = i 2 S(i)=\sum_{i=1}^{n}i^2\varphi(i), g(i)=i^2 S(i)=i=1ni2φ(i),g(i)=i2

一、例题

二、代码及思路

1.思路

直接用 ϕ \phi ϕ μ \mu μ 的板子即可

2.代码

代码如下:

#include <iostream>
#include <map>
#define int long long
using namespace std;
const int maxn = 5e6;
int miu[maxn], phi[maxn];
int premiu[maxn], prephi[maxn];
int prime[maxn], pn;
bool isp[maxn];
map<int, int> Smiu;
map<int, int> Sphi;
void table() {
  for (int i = 1; i < maxn; i++) isp[i] = true;
  miu[1] = phi[1] = 1, isp[1] = false;
  for (int i = 2; i < maxn; i++) {
    if (isp[i]) {
      prime[pn++] = i;
      miu[i] = -1, phi[i] = i - 1;
    }
    for (int j = 0; j < pn && i * prime[j] < maxn; j++) {
      isp[i * prime[j]] = false;
      if (i % prime[j] == 0) {
        miu[i * prime[j]] = 0;
        phi[i * prime[j]] = phi[i] * prime[j];
        break;
      }
      miu[i * prime[j]] = -miu[i];
      phi[i * prime[j]] = phi[i] * phi[prime[j]];
    }
  }
  for (int i = 1; i < maxn; i++)
    premiu[i] = miu[i] + premiu[i - 1], prephi[i] = phi[i] + prephi[i - 1];
}
int getmiu(int n) {
  if (n < maxn) return premiu[n];
  if (Smiu[n]) return Smiu[n];
  int res = 1;
  for (int l = 2, r; l <= n; l = r + 1) {
    r = n / (n / l);
    res -= (r - l + 1) * getmiu(n / l);
  }
  return Smiu[n] = res;
}
int getphi(int n) {
  if (n < maxn) return prephi[n];
  if (Sphi[n]) return Sphi[n];
  int res = n * (n + 1) / 2;
  for (int l = 2, r; l <= n; l = r + 1) {
    r = n / (n / l);
    res -= (r - l + 1) * getphi(n / l);
  }
  return Sphi[n] = res;
}
signed main() {
  // freopen("in.txt", "r", stdin);
  // freopen("out.txt", "w", stdout);
  int t, n;
  table();
  scanf("%lld", &t);
  while (t--) {
    scanf("%lld", &n);
    printf("%lld %lld\n", getphi(n), getmiu(n));
  }
  return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值