bzoj 3944 sum
求phi和mu的前缀和,n<2^31
首先orz叉院lyp大神的讲义。
以下为lyp大神讲义的公式推导过程(我不生产公式,我只是大神的搬运工~~)
假设我们需要求f(x)的前缀和
令g(n)=sigma f(d) (满足d|n)
F(n)=sigma f(i) (1<=i<=n)
我们可以得到sigma g(i) (1<=i<=n) = sigma f(i)*[n/i] (1<=i<=n, [x]表示x向下取整) = sigma F(n/i) (1<=i<=n)
移项可得F[n] = sigma g(i) (1<=i<=n) - sigma F(n/i) (2<=i<=n)
直接递归复杂度为O( n^(3/4) ) (不要问我为什么。。。)
据说我们可以先将n^(2/3)内的答案先删出来,再记忆划搜索一下就可以做到O( n^(2/3) )
//虽然搞了这么多,还算是复习了一下线性筛法。。。。
#include <map>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int Maxn=1000005;
typedef long long LL;
LL mu[Maxn],phi[Maxn],ans1,ans2,n;
int prime[Maxn],i,j,tot,Case;
bool check[Maxn];
map <int,LL> vx,vy;
void init(){
mu[1]=1; phi[1]=1;
for (i=2;i<Maxn;i++){
if (!check[i]){
prime[++tot]=i;
mu[i]=-1;
phi[i]=i-1;
}
for (j=1;j<=tot;j++){
if (prime[j]*i>=Maxn) break;
check[prime[j]*i]=1;
if (i%prime[j]==0){
mu[i*prime[j]]=0;
phi[i*prime[j]]=phi[i]*prime[j];
break;
} else
{
mu[i*prime[j]]=-mu[i];
phi[i*prime[j]]=phi[i]*(prime[j]-1);
}
}
}
for (i=1;i<Maxn;i++){
mu[i]+=mu[i-1];
phi[i]+=phi[i-1];
}
}
void calc(LL n,LL &x,LL &y){
if (n<Maxn) {x = phi[n], y = mu[n];return;}
if (vx[n]!=0){
x = vx[n]; y=vy[n];
return;
}
x = n*(n+1)/2; y = 1;
LL i, j, t1, t2;
for (i=2;i<=n;i=j+1){
j = n/(n/i);
calc(n/i,t1,t2);
x = x-t1*(j-i+1);
y = y-t2*(j-i+1);
}
vx[n] = x; vy[n] = y;
}
int main(){
freopen("3944.in","r",stdin);
freopen("3944.out","w",stdout);
init();
scanf("%d",&Case);
while (Case--){
scanf("%lld",&n);
calc(n,ans1,ans2);
printf("%lld %lld\n",ans1,ans2);
}
return 0;
}