luogu P4213 【模板】杜教筛(Sum)
https://www.luogu.com.cn/problem/P4213
才发现杜教筛是个这么naive的东西,我觉得我也可以发明出来
给出狄利克雷卷积的几个重要性质
\(\mu *I=ϵ\)
\(\phi*I=id\)
\(\mu*id=\phi\)
满足结合律和交换律
设\(\large S(n)=\sum\limits_{i=0}^n f(i)\)
我们找一个积性函数\(g\)和\(f\)卷起来
\(\large \sum\limits_{i=1}^{n} (f*g) (i)\)
\(\large =\sum\limits_{i=1}^{n} \sum\limits_{d|i}f(d)g(i/d)\)
\(\large =\sum\limits_{d=1}^n g(d) \sum\limits_{i=1}^{n/d} f(i)\)
\(\large = \sum\limits_{d=1}^n g(d) S(n/d)\)
我们考虑\(g(1)S(n)\)是什么?
可以发现$$g(1)S(n)=\sum\limits_{i=1}^n (f*g)(i)-\sum\limits_{i=2}^n g(d)S(n/d)$$
对于\(f=\mu\)令\(g=I\)可以得到
因为\(\mu * I= ϵ\)
所以
\(S(n)=1-\sum\limits_{i=2}^n g(d)S(n/d)\)
递归做即可
另外一个同样是卷上\(I\),加个记忆话,和前面暴力筛即可
code:
#include<bits/stdc++.h>
#define N 5000050
#define ll long long
using namespace std;
const int lim = 5000000;
ll mu[N], phi[N];
int prime[N], vis[N], sz;
void init(int n) {
mu[1] = phi[1] = 1;
for(int i = 2; i <= n; i ++) {
if(!vis[i]) {
prime[++ sz] = i;
phi[i] = i - 1, mu[i] = -1;
}
for(int j = 1; j <= sz && 1ll * prime[j] * i <= n; j ++) {
vis[prime[j] * i] = 1;
if(i % prime[j] == 0) {
phi[i * prime[j]] = phi[i] * prime[j];
break;
}
mu[i * prime[j]] = - mu[i];
phi[i * prime[j]] = phi[i] * phi[prime[j]];
}
}
for(int i = 1; i <= n; i ++) mu[i] += mu[i - 1], phi[i] += phi[i - 1];
}
map<int, ll> smu;
map<int, ll> sphi;
ll getmu(int n) {
if(n <= lim) return mu[n];
if(smu[n]) return smu[n];
ll ret = 1ll;
for(ll l = 2, r = 0; l <= n; l = r + 1) {
r = n / (n / l);
ret -= (r - l + 1) * getmu(n / l);
} return smu[n] = ret;
}
ll getphi(int n) {
if(n <= lim) return phi[n];
if(sphi[n]) return sphi[n];
ll ret = 1ll * n * (n + 1ll) / 2;
for(ll l = 2, r = 0; l <= n; l = r + 1) {
r = n / (n / l);
ret -= (r - l + 1) * getphi(n / l);
} return sphi[n] = ret;
}
int n, t;
void solve() {
scanf("%d", &n);
printf("%lld %lld\n", getphi(n), getmu(n));
}
int main() {
init(lim);
scanf("%d", &t);
while(t --) solve();
return 0;
}