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;
}