杜教筛:Bzoj3944: sum

题意

\(\sum_{i=1}^{n}\varphi(i)和\sum_{i=1}^{n}\mu(i)\)
\(n <= 2^{31}-1\)

不会做啊。。。
只会线性筛,显然不能线性筛
这个时候就需要杜教筛

怎么筛

先看一下狄利克雷卷积
假设我们要求\(F(i)=\sum_{i=1}^{n}f(n)\)\(n(10^{11}左右)\)比较大不能线性筛时考虑杜教筛
套路的推导:
先随意找一个函数\(g(i)\)\(f(i)\)求狄利克雷卷积:
$$(g * f)(n) = \sum_{d|n} g(d)f(\frac{n}{d})$$
它的前缀和就是
$$\sum_{i=1}^{n}\sum_{d|i} g(d)f(\frac{i}{d})$$
现在要想办法向\(F(n)\)上靠:
$$原式=\sum_{d=1}{n}\sum_{d|i}g(d)f(\frac{i}{d})=\sum_{d=1}\sum_{i=1}^{\lfloor\frac{n}{d}\rfloor}g(d)f(i)$$
$$=\sum_{d=1}{n}g(d)\sum_{i=1}\rfloor}f(i)=\sum_{d=1}^{n}g(d)F(\lfloor\frac{n}{d}\rfloor)$$
所以

\[g(1)F(n)=(g * f)(n)-\sum_{d=2}^{n}g(d)F(\lfloor\frac{n}{d}\rfloor) \]

\(g(1)=1时最好\)
如果能够快速的对\(g\)\(g * f\)求和,那么就能在\(O(n^{\frac{3}{4}})的时间内计算出F(n)\)复杂度证明略不会
如果\(f\)为积性函数,还可以线性筛出\(F\)的前若干项(\(n^{\frac{2}{3}}\)最优)降低复杂度

Sol

再看这道题

先看\(\varphi\):

\(\phi(i)=\sum_{i=1}^{n}\varphi(i)\)
按上面的来
$$g(1)\phi(n)=\sum_{i=1}{n}(g*\varphi)(i)-\sum_{i=2}g(d)\phi(\lfloor\frac{n}{d}\rfloor)$$
有个定理\(\sum_{d|n}\varphi(d)=n\)
\(g(i)=1\)所以
$$\sum_{i=1}{n}(g*\varphi)(i)=\sum_{i=1}\sum_{d|i}\varphi(\frac{i}{d})=\frac{n*(n + 1)}{2}$$
线性筛一部分,带进去递归处理即可

再看\(\mu\):

\(U(n)=\sum_{i=1}^{n}\mu(i)\)
一样的套路得到
$$g(1)U(n)=\sum_{i=1}{n}\sum_{d|i}g(d)\mu(\frac{i}{d})-\sum_{i=2}g(d)U(\lfloor\frac{n}{d}\rfloor)$$
注意到\(\sum_{d|n}\mu(d)=[n=1]\),直接令\(g(i)=1\)
$$U(n)=1-\sum_{i=2}^{n}g(d)U(\lfloor\frac{n}{d}\rfloor)$$
一样的递归处理即可

一定要记忆化(我用的map)

# include <bits/stdc++.h>
# define RG register
# define IL inline
# define Fill(a, b) memset(a, b, sizeof(a))
using namespace std;
typedef long long ll;
const int _(5e6 + 1);

IL ll Read(){
    RG ll x = 0, z = 1; RG char c = getchar();
    for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
    for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
    return x * z;
}

int prime[_], num;
ll phi[_], mu[_];
map <int, ll> Phi, Mu;
bool isprime[_];

IL void Prepare(){
	isprime[1] = 1; phi[1] = mu[1] = 1;
	for(RG int i = 2; i < _; ++i){
		if(!isprime[i]){  prime[++num] = i; mu[i] = -1; phi[i] = i - 1;  }
		for(RG int j = 1; j <= num && i * prime[j] < _; ++j){
			isprime[i * prime[j]] = 1;
			if(i % prime[j]){  mu[i * prime[j]] = -mu[i]; phi[i * prime[j]] = phi[i] * (prime[j] - 1);  }
			else{  mu[i * prime[j]] = 0; phi[i * prime[j]] = phi[i] * prime[j]; break;  }
		}
	}
	for(RG int i = 2; i < _; ++i) mu[i] += mu[i - 1], phi[i] += phi[i - 1];
}

IL ll Sumphi(RG ll n){
	if(n < _) return phi[n];
	if(Phi[n]) return Phi[n];
	RG ll ans = n * (n + 1) / 2;
	for(RG ll i = 2, j; i <= n; i = j + 1){
		j = n / (n / i);
		ans -= (j - i + 1) * Sumphi(n / i);
	}
	return Phi[n] = ans;
}

IL ll Summu(RG ll n){
	if(n < _) return mu[n];
	if(Mu[n]) return Mu[n];
	RG ll ans = 1;
	for(RG ll i = 2, j; i <= n; i = j + 1){
		j = n / (n / i);
		ans -= (j - i + 1) * Summu(n / i);
	}
	return Mu[n] = ans;
}

int main(RG int argc, RG char* argv[]){
	Prepare();
	for(RG int T = Read(); T; --T){
		RG ll n = Read();
		printf("%lld %lld\n", Sumphi(n), Summu(n));
	}
    return 0;
}

posted @ 2018-01-16 19:24  Cyhlnj  阅读(173)  评论(0编辑  收藏  举报