洛谷4213:【模板】杜教筛
洛谷4213:【模板】杜教筛
题意:
- 给定一个\(n\leq 10^9\)。
- 求:
- \(ans1=\sum_{i=1}^n\phi(i)\).
- \(ans2=\sum_{i=1}^n\mu(i)\).
思路:
- 杜教筛。
- 狄利克雷卷积的性质:
- \(\mu*I=\epsilon\),\(\phi*I=id\),\(\mu*id=\phi\)。
- 杜教筛核心公式:
- \(g(1)S(n)=\sum_{i=1}^n(f*g)(i)-\sum_{i=2}^ng(i)S(\frac{n}{i})\)。
欧拉函数:
-
\(\phi*I=id\),取\(f=\phi,g=I,f*g=id\)。
-
又知道\(id\)是单位函数:\(id(n)=n\)。
-
则\(f*g\)的前缀和为\(\frac{n*(n+1)}{2}\)。
-
后半部分用数论分块求解。
莫比乌斯函数:
- \(u*I=\epsilon\),取\(f=\mu,g=I,f*g=\epsilon\)。
- \(g\)和\(f*g\)的前缀和都太容易求了。
- \(g\)的前缀和等于\(n\),\(f*g\)的前缀和等于\(1\)。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 5e6;
bool vis[maxn+10];
int primes[maxn+10], cnt;
ll phi[maxn+10], mu[maxn+10];
void init(int n)
{
phi[1] = 1;
mu[1] = 1;
for(int i = 2; i <= n; i++)
{
if(!vis[i])
{
primes[++cnt] = i;
phi[i] = i-1;
mu[i] = -1;
}
for(int j = 1; primes[j] <= n/i; j++)
{
vis[primes[j]*i] = 1;
if(i % primes[j] == 0)
{
phi[primes[j]*i] = phi[i] * primes[j];
break;
}
else
{
phi[primes[j]*i] = phi[i]*(primes[j]-1);
mu[i*primes[j]] = -mu[i];
}
}
}
for(int i = 1; i <= n; i++)
{
mu[i] += mu[i-1];
phi[i] += phi[i-1];
}
}
unordered_map<int, ll> Smu, Sphi;
ll getSphi(int n)
{
//提前筛好的
if(n <= maxn) return phi[n];
//记忆化
if(Sphi[n]) return Sphi[n];
//phi * I = id 的前缀和
ll res = 1ll*n*(n+1)/2;
for(int l = 2, r; l <= n; l = r+1)
{
r = n/(n/l);
res -= (r-l+1)*getSphi(n/l);
}
return Sphi[n] = res;
}
ll getSmu(int n)
{
if(n <= maxn) return mu[n];
if(Smu[n]) return Smu[n];
//原函数的前缀和是1
//f = mu, g = I
ll res = 1;
for(int l = 2, r; l <= n; l = r+1)
{
r = n/(n/l);
res -= (r-l+1)*getSmu(n/l);
}
return Smu[n] = res;
}
int main()
{
init(maxn);
int T; scanf("%d", &T);
while(T--)
{
int n; scanf("%d", &n);
printf("%lld %lld\n", getSphi(n), getSmu(n));
}
return 0;
}