前置姿势
杜教筛
下面考虑一个问题,你手里有一个积性函数(f(n)),定义(S_f(n)=sumlimits_{i=1}^n f(i))(即(f)的前缀和)。
对与一个(n),你现在要快速求出(S_f(n)),假设线性筛都死了
我们先随便找一个积性函数(g),然后跟(f)卷上,得到第三个积性函数(h=f*g),那么
考虑求(S_h(n)),推柿子
因为(g)是积性函数,必然有(g(1)=1),所以
如果说(h)和(g)的前缀和能快速求出来的话,后面那一部分我们只要数论分块就好了,这样就能做到在非线性时间内求出(f)的前缀和。
具体的复杂度并不会算qwq......好像说裸的杜教筛是(O(n^{3/4}))的,但如果筛出前(n^{2/3})个前缀和后就变成(O(n^{2/3}))了,总之尽可能多的线性筛出能处理的答案,这样复杂度并不会差,杜教筛的时候记忆化就好了。
扯了那么多,来看板子题吧:[模板]杜教筛
让你求(varphi)和(mu)的前缀和,(n le 2^{31}-1),所以说线性筛死了......
考虑杜教筛,我们先来看莫比乌斯函数好了
考虑到(sumlimits_{dmid n} mu(d) = varepsilon(n)),即(mu*I=varepsilon),取(g=I),与(mu)卷积后得到单位元函数,我们知道对于(nge 1),(S_{varepsilon}(n)equiv 1),这个就非常nice,然后(I)的前缀和就是(n),套柿子
记忆化然后数论分块即可。
然后是(varphi),有式子(sumlimits_{dmid n} varphi(d)=n),这里给出一个简(gan)单(xing)的证(li)明(jie)
显然(n=sumlimits_{dmid n} sumlimits_{i=1}^n [gcd(n,i)=d]),后面的柿子也就是(sumlimits_{i=1}^{n/d} [gcd(n,i)=1]=varphi(frac{n}{d})),所以(n=sumlimits_{dmid n} varphi(frac{n}{d})=sumlimits_{dmid n} varphi(d))
于是(varphi*I=id),怎么有是I,于是取(g=I),得到(h=varphi*I=id),套柿子
同样记忆化+数论分块就好了。
#include <bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for (int i=(a);i<(b);++i)
#define per(i,a,b) for (int i=(a)-1;i>=(b);--i)
#define mp make_pair
#define pb push_back
#define fi first
#define se second
#define all(x) (x).begin(),(x).end()
#define SZ(x) ((int)(x).size())
typedef double db;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
typedef vector<int> VI;
const int maxn=2e6,N=maxn+10;
int p[N],pn;
ll mu[N],phi[N],smu[N],sphi[N];
bitset<N> vis;
inline ll s1(ll l,ll r) {return 1ll*(r-l+1)*(l+r)/2;}
void init(int n) {
mu[1]=1,phi[1]=1;
rep(i,2,n+1) {
if(!vis[i]) {
mu[i]=-1,phi[i]=i-1;
p[pn++]=i;
}
for(int j=0;j<pn&&i*p[j]<=n;j++) {
vis[i*p[j]]=1;
if(i%p[j]==0) {
mu[i*p[j]]=0;
phi[i*p[j]]=phi[i]*p[j];
break;
}
else mu[i*p[j]]=-mu[i],phi[i*p[j]]=phi[i]*phi[p[j]];
}
}
rep(i,1,n+1) smu[i]=smu[i-1]+mu[i],sphi[i]=sphi[i-1]+phi[i];
}
map<int,ll> phisum,musum;
ll Sphi(int n) {
if(n<=maxn) return sphi[n];
if(phisum.count(n)) return phisum[n];
ll ans=s1(1,n);
for(ll l=2,r=0;l<=n;l=r+1) {
r=n/(n/l);
ans-=Sphi(n/l)*(r-l+1);
}
return phisum[n]=ans;
}
ll Smu(int n) {
if(n<=maxn) return smu[n];
if(musum.count(n)) return musum[n];
ll ans=1;
for(ll l=2,r=0;l<=n;l=r+1) {
r=n/(n/l);
ans-=Smu(n/l)*(r-l+1);
}
return musum[n]=ans;
}
int main() {
init(maxn);
int _,n; for(scanf("%d",&_);_;_--) {
scanf("%d",&n);
printf("%lld %lld
",Sphi(n),Smu(n));
}
return 0;
}