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;
} 
posted @ 2021-12-29 08:23  lahlah  阅读(25)  评论(0编辑  收藏  举报