[51Nod 1244] - 莫比乌斯函数之和 & [51Nod 1239] - 欧拉函数之和 (杜教筛板题)

[51Nod 1244] - 莫比乌斯函数之和

i=1Nμ(i)\sum_{i=1}^Nμ(i)
开推
dnμ(d)=[n==1]\sum_{d|n}\mu(d)=[n==1]
移项
μ(d)=[n==1]dn,d<nμ(d)S(N)=i=1Nμ(i)=i=1N([i==1]di,d<iμ(d))=1i=1Ndi,d<iμ(d)\mu(d)=[n==1]-\sum_{d|n,d<n}\mu(d)\newline \therefore S(N)=\sum_{i=1}^Nμ(i)=\sum_{i=1}^N([i==1]-\sum_{d|i,d<i}\mu(d))\newline =1-\sum_{i=1}^N\sum_{d|i,d<i}\mu(d) \newline
此处是杜教筛的精髓,也是整除分块优化的精髓(或者说是转换方法),枚举i是d的多少倍
S(N)=1id=2Nd=1,1<idNidμ(d)S(N)=1-\sum_{{\lfloor{\frac id}\rfloor} =2}^N\sum_{d=1,1<{\lfloor{\frac id}\rfloor}}^{\lfloor \frac N{\lfloor{\frac id}\rfloor} \rfloor}\mu(d)
k=idk={\lfloor{\frac id}\rfloor},则有
S(N)=1k=2Nd=1Nkμ(d)=1k=2NS(Nk)S(N)=1-\sum_{k=2}^N\sum_{d=1}^{\lfloor \frac Nk\rfloor}\mu(d) =1-\sum_{k=2}^NS({\lfloor \frac Nk\rfloor})
那么就可以用线性筛出前面一部分,再整除分块优化递归处理
杜教筛无预处理的时间复杂度是Θ(n34)\Theta(n^{\frac 34})
预处理一定范围内的答案后就能降到Θ(n23)\Theta(n^{\frac 23})
然而我们此处并不需要多小的时间复杂度…随便处理一个10710^7就能过了

AC code
#include <cstdio>
#include <map>
#include <cstring>
#include <algorithm>
using namespace std;
#define LL long long
const int MAXP = 1e7+1;

int Prime[MAXP/10], Cnt, mu[MAXP], sum[MAXP];
bool IsnotPrime[MAXP];
map<LL,int>S;
void init(int N)
{
	mu[1] = 1;
	for(int i = 2; i <= N; ++i)
	{
		if(!IsnotPrime[i]) Prime[++Cnt] = i, mu[i] = -1;
		for(int j = 1; j <= Cnt && i * Prime[j] <= N; ++j)
		{
			IsnotPrime[i * Prime[j]] = 1;
			if(i % Prime[j] == 0) { mu[i * Prime[j]] = 0; break; }
			mu[i * Prime[j]] = -mu[i];
		}
	}
	for(int i = 1; i <= N; i++) sum[i] = sum[i-1] + mu[i];
}

inline int solve(LL N)
{
	if(N < MAXP) return sum[N];
	if(S.count(N)) return S[N];//记忆化,其实此处可以Hash或者用
	int ret = 1;
	for(LL i = 2, j; i <= N; i=j+1)
	{
		j = N/(N/i);
		ret -= (j-i+1) * solve(N/i);
	}
	return S[N] = ret;
}

int main ()
{
    LL n, m; init(MAXP-1);
    scanf("%lld%lld", &n, &m);
    printf("%d\n", solve(m)-solve(n-1));
}

[51Nod 1239] - 欧拉函数之和

i=1Nφ(i)\sum_{i=1}^Nφ(i)
开推,有
dnφ(d)=n \sum_{d|n}φ(d)=n
移项
φ(n)=ndn,d&lt;nφ(d)S(N)=i=1N(idi,d&lt;iφ(d))=N(N+1)2i=1Ndi,d&lt;iφ(d)=N(N+1)2id=2Nd=1Nidφ(d) φ(n)=n-\sum_{d|n,d&lt;n}φ(d)\newline \therefore S(N)=\sum_{i=1}^N(i-\sum_{d|i,d&lt;i}φ(d))\newline =\frac{N*(N+1)}2-\sum_{i=1}^N\sum_{d|i,d&lt;i}φ(d)\newline =\frac{N*(N+1)}2-\sum_{{\lfloor\frac id\rfloor}=2}^N\sum_{d=1}^{{\lfloor\frac N{{\lfloor\frac id\rfloor}}\rfloor}}φ(d)\newline
k=idk={\lfloor{\frac id}\rfloor},则有
S(N)=N(N+1)2k=2Nd=1Nkφ(d)=N(N+1)2k=2NS(Nk) S(N)=\frac{N*(N+1)}2-\sum_{k=2}^N\sum_{d=1}^{{\lfloor\frac Nk\rfloor}}φ(d)\newline =\frac{N*(N+1)}2-\sum_{k=2}^NS({\lfloor\frac Nk\rfloor})\newline
然后…就没有了.像上一道题一样处理就完了

AC Code
#include <cstdio>
#include <map>
#include <cstring>
#include <algorithm>
using namespace std;
#define LL long long
const int MAXP = 5e6 + 1;
const int mod = 1e9 + 7;
const int inv2 = 500000004;

int Prime[MAXP/10], Cnt, phi[MAXP];
LL sum_phi[MAXP];
bool IsnotPrime[MAXP];

void init(int N)
{
	phi[1] = 1;
	for(LL i = 2; i <= N; ++i)
	{
		if(!IsnotPrime[i]) Prime[++Cnt] = i, phi[i] = i-1;
		for(LL j = 1; j <= Cnt && i * Prime[j] <= N; ++j)
		{
			IsnotPrime[i * Prime[j]] = 1;
			if(i % Prime[j] == 0)
			{
				phi[i * Prime[j]] = phi[i] * Prime[j];
				break;
			}
			phi[i * Prime[j]] = phi[i] * (Prime[j]-1);
		}
	}
	for(int i = 1; i <= N; i++)
		sum_phi[i] = ((sum_phi[i-1] + phi[i]) % mod + mod) % mod;
}
map<LL, LL> S_phi;
inline LL solve_phi(LL N)
{
	if(N < MAXP) return sum_phi[N];
	if(S_phi.count(N)) return S_phi[N];
	LL ret = (N%mod + 1) * (N%mod) % mod * inv2 % mod;
	for(LL i = 2, j; i <= N; i=j+1)
	{
		j = N/(N/i);
		ret = (ret - (j-i+1) % mod * solve_phi(N/i) % mod) % mod;
	}
	return S_phi[N] = ((ret + mod) % mod);
}
int main ()
{
	init(MAXP-1); LL n;
	scanf("%lld", &n);
	printf("%lld\n", solve_phi(n));
}
posted @ 2019-12-14 14:52  _Ark  阅读(94)  评论(0编辑  收藏  举报