杜教筛入门

f f f 的前缀和(不要求 f f f 为积)

考虑 h = f ∗ g h=f*g h=fg,若 h , g h,g h,g 前缀和都好求,那 f f f 的前缀和 s s s 是好求的

∑ i = 1 n h i = ∑ i j ≤ n f i g j \sum_{i=1}^n h_i=\sum_{ij\le n}f_ig_j i=1nhi=ijnfigj

∑ i = 1 n h i = ∑ i ≤ n g i ∑ d = 1 ⌊ n i ⌋ f d \sum_{i=1}^n h_i=\sum_{i\le n}g_i\sum_{d=1}^{\left \lfloor\frac ni\right \rfloor}f_d i=1nhi=ingid=1infd

∑ i = 1 n h i = ∑ i ≤ n g i s ( ⌊ n i ⌋ ) \sum_{i=1}^n h_i=\sum_{i\le n}g_is(\left \lfloor\frac n i\right \rfloor) i=1nhi=ingis(in)

若满足 g 1 ≠ 0 g_1\ne 0 g1=0,则:

s ( n ) = ∑ i = 1 n h i − ∑ d = 2 n g i s ( ⌊ n d ⌋ ) g 1 s(n)=\frac{\sum_{i=1}^nh_i-\sum_{d=2}^ng_is(\left \lfloor\frac n d\right \rfloor)}{g_1} s(n)=g1i=1nhid=2ngis(dn)

我们计算时直接整除分块即可

加速:根号分治

s ( x ) s(x) s(x) 时,对于 x ≤ B x\le B xB,我们直接线性筛预处理。

对于 x > B x>B x>B 时,我们记忆化。注意到所有遍历的 x x x 满足 ⌊ n x ⌋ \left \lfloor\dfrac n x\right \rfloor xn 互不相同,所以直接拿数组来存即可。

可以证明在 B = n 2 3 B=n^{\frac 2 3} B=n32 时可以达到最优复杂度 O ( 2 3 ) O(\frac 2 3) O(32)

杜教筛的难度就是构造 g , h g,h g,h,剩下直接套式子就行了。


#include<bits/stdc++.h>
using namespace std;
#ifdef LOCAL
 #define debug(...) fprintf(stdout, ##__VA_ARGS__)
#else
 #define debug(...) void(0)
#endif
#define int long long
inline int read(){int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;
ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+
(x<<3)+(ch^48);ch=getchar();}return x*f;}
#define Z(x) (x)*(x)
#define pb push_back
#define fi first
#define se second
//#define M
//#define mo
#define N 1000000
int n, m, i, j, k, T;
int mu[N], pri[N], L, ans1, ans2; 
int z[N], top, smu[N]; 
int sph[N], phi[N]; 
map<int, int>Phi, Mu; 

int find_mul(int n) {
	if(n <= L) return smu[n]; 
	if(Mu[n]) return Mu[n]; 
	int ans = 1, l, r; 
	for(l = 2, r = 0; l <= n; l = r + 1) {
		r = min(n, n / (n / l));
//		debug("%lld --> %lld\n", n / l, n); 
		ans -= find_mul(n / l) * (r - l + 1); 
	}
	return Mu[n] = ans; 
}

int find_phi(int n) {
	if(n <= L) return sph[n]; 
	if(Phi[n]) return Phi[n]; 
	int ans = (n + 1) * n / 2, l, r; 
	for(l = 2, r = 0; l <= n; l = r + 1) {
		r = min(n, n / (n / l));
//		debug("%lld --> %lld [%lld]\n", n / l, n, r - l + 1); 
		ans -= find_phi(n / l) * (r - l + 1); 
	}
//	debug("Phi[%lld] = %lld\n", n, ans); 
	return Phi[n] = ans; 
}

signed main()
{
	#ifdef LOCAL
	  freopen("in.txt", "r", stdin);
	  freopen("out.txt", "w", stdout);
	#endif
//	srand(time(NULL));
	L = N - 1; 
		memset(pri, -1, sizeof(pri)); 
//		memset(mu, 0, sizeof(mu)); 
//		memset(smu, 0, sizeof(smu)); 
//		memset(phi, 0, sizeof(phi)); 
//		memset(Phi, 0, sizeof(Phi)); 
//		memset(sph, 0, sizeof(sph)); 
//		memset(Mu, 0, sizeof(Mu)); 
		top = 0; 
		mu[1] = smu[1] = phi[1] = sph[1] = 1; pri[1] = 0; 
		for(i = 2; i <= L; ++i){
			if(pri[i]) {
				z[++top] = i; mu[i] = -1; phi[i] = i - 1; 
			}
			for(j = 1; j <= top && i * z[j] <= L; ++j) {
				int x = i * z[j]; pri[x] = 0; 
				mu[x] = mu[z[j]] * mu[i]; 
				phi[x] = phi[z[j]] * phi[i]; 
				if(i % z[j] == 0) { 
					phi[x] = phi[i] * z[j]; 
					mu[x] = 0; break; 
				}
			}
			smu[i] = smu[i - 1] + mu[i]; 
			sph[i] = sph[i - 1] + phi[i]; 
//			debug("mul[%lld] = %lld %lld\n", i, mu[i], smu[i]); 
//			debug("phi[%lld] = %lld %lld\n", i, phi[i], sph[i]); 
		}
	T=read();
	while(T--) {
		n = m = read(); 
		ans1 = find_phi(n); 
		ans2 = find_mul(n); 
		printf("%lld %lld\n", ans1, ans2); 
	}
	
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值