杜教筛

杜教筛的作用为计算积性函数前缀和

即求$sum_{i=1}^n f(i)$ f(i)为积性函数

构造:$h = f * g$

即:$S(n)=sum_{i=1}^nf(i)$

求$sum_{i=1}^nh(i)$

开始推式子:

$sum{i=1}^nh(i)=sum{i=1}^nsum_{d|i}g(d) * f(n/d)$

$=sum{d=1}^ng(d)sum{i=1}^{n/d}f(i)$

即:

$sum{i=1}^nh(i)=sum{d=1}^ng(d)*S(n/d)$

即:

$sum{i=1}^nh(i)=g(1)S(n)+sum{i=2}^ng(d)S(n/d)$

$g(1)*S(n)=sum{i=1}^n-sum{d=2}^ng(d)S(n/d)$

其中:

$h(i)=(f*g)(i)$

杜教筛应用:

1.求$S(n)=sum_{i=1}^nφ(i)$

因为:$φ * I = id$

代入可得:

$S(n)=sum{i=1}^n i - sum{d=2}^nS(n/d)$

2.求$S(n)=sum_{i=1}^nu(i)$

因为$u * I = e$

易得:

$S(n) = 1 - sum_{i=1}^nS(n/d)$

3.求$S(n)=sum_{i=1}^ni*φ(i)$

首先

狄利克雷卷积:

$sum_{d|n}(d φ(n)) g(n/d)$;

令g配成id:

$sum_{d|n}(d φ(d)) g(n/d)$

即:

$sum{d|n}(dφ(d) n / d=sum{d|n}n * φ(d)=n^2$

即:

$S(n)=sum{i=1}^ni^2 - sum{d=2}^n d * S(n/d)$

附上洛谷模板题代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83

#define LL long long

using namespace std;

inline int ()
{
int X = 0,w = 0;char ch = 0;
while(!isdigit(ch)){w |= ch == '-';ch = getchar();}
while(isdigit(ch)){X = (X << 3) + (X << 1) + ch - '0';ch = getchar();}
return w ? -X:X;
}

const int N = 5e6 + 501;
int u[N];
LL phi[N], s1[N];
int prime[N], tot;
bool flag[N];
int s2[N], n;
map<LL,LL>w1;
map<int,int>w;

void get_prime(int n)
{
flag[1] = 1;
u[1] = 1;
phi[1] = 1;
for(int i = 2;i <= n;i ++)
{
if(!flag[i])prime[++ tot] = i,u[i] = -1,phi[i] = i - 1;
for(int j = 1;j <= tot && i * prime[j] <= n;j ++)
{
flag[i * prime[j]] = 1;
if(i % prime[j] == 0)
{
u[i * prime[j]] = 0;
phi[i * prime[j]] = phi[i] * prime[j];
break;
}
u[i * prime[j]] = -u[i];
phi[i * prime[j]] = phi[i] * (prime[j] - 1);
}
}
for(int i = 1;i <= n;i ++)
s1[i] = s1[i - 1] + phi[i],s2[i] = s2[i - 1] + u[i];
}

int get_mu(int x)
{
if(x <= 5000000)return s2[x];
if(w[x])return w[x];
int ans = 1;
for(int l = 2,r;l <= x;l = r + 1)
{
r = x / (x / l);
ans -= (r - l + 1) * get_mu(x / l);
}
return w[x] = ans;
}

LL get_phi(LL x)
{
if(x <= 5000000)return s1[x];
if(w1[x])return w1[x];
LL ans = x * (x + 1) / 2;
for(LL l = 2,r;l <= x;l = r + 1)
{
r = x / (x / l);
ans -= (r - l + 1) * get_phi(x / l);
}
return w1[x] = ans;
}

signed main()
{
int t = read();
get_prime(5000000);
for(int i = 1;i <= t;i ++)
{
int x = read();
printf("%lld %dn",get_phi(x),get_mu(x));
}
}