模板 - 杜教筛
Posted inko
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了模板 - 杜教筛相关的知识,希望对你有一定的参考价值。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
//N为n^(2/3)最快
int n;
const int MAXN=5e6;
unordered_map<int,int> Smu;
unordered_map<int,ll> Sphi;
ll sump[MAXN+5];
int sumu[MAXN+5];
int pri[MAXN+5],pritop;
bool notpri[MAXN+5];
//pritop从1开始计数
void sieve(int n) {
notpri[1]=sump[1]=sumu[1]=1;
for(int i=2; i<=n; i++) {
if(!notpri[i])
pri[++pritop]=i,sump[i]=i-1,sumu[i]=-1;
for(int j=1; j<=pritop&&i*pri[j]<=n; j++) {
notpri[i*pri[j]]=1;
//略有不同
if(i%pri[j])
sump[i*pri[j]]=sump[i]*sump[pri[j]],sumu[i*pri[j]]=-sumu[i];
else {
sump[i*pri[j]]=sump[i]*pri[j],sumu[i*pri[j]]=0;;
break;
}
}
}
for(int i=2; i<=n; i++) {
sump[i]+=sump[i-1];
sumu[i]+=sumu[i-1];
}
}
//杜教筛莫比乌斯函数
inline ll GetSumu(int n) {
if(n <= MAXN)
return sumu[n]; // sumu是提前筛好的前缀和
if(Smu.count(n))
return Smu[n]; // 记忆化
ll ret = 1ll; // 单位元的前缀和就是 1
for(int l = 2, r; r<2147483647&&l <= n; l = r + 1) {
r = n / (n / l);
ret -= (r - l + 1) * GetSumu(n / l);
// (r - l + 1) 就是 I 在 [l, r] 的和
}
return Smu[n] = ret; // 记忆化
}
//杜教筛欧拉函数
inline ll GetSphi(int n) {
if(n <= MAXN)
return sump[n]; // 提前筛好的
if(Sphi.count(n)) return Sphi[n]; // 记忆化
ll ret = n;
if(ret%2==0)
ret=(ret/2)* (1ll+n);
else
ret*=((1ll+n)/2);
// f * g = id 的前缀和
for(int l = 2, r; r<2147483647&&l <= n; l = r + 1) {
r = n / (n / l);
ret -= (r - l + 1) * GetSphi(n / l);
// 同上,因为两个的 g 都是 I
}
return Sphi[n] = ret; // 记忆化
}
int main() {
sieve(MAXN);
int t;
scanf("%d",&t);
while(t--) {
scanf("%d",&n);
printf("%lld %lld
",GetSphi(n),GetSumu(n));
}
}
以上是关于模板 - 杜教筛的主要内容,如果未能解决你的问题,请参考以下文章