bzoj 3944: Sum

来源:互联网 发布:数据报网络 编辑:程序博客网 时间:2024/05/17 22:06

题意,求ϕμ的前缀和。

题解:

杜教筛模板。
先说μ的方法。
因为ii|nμ(i)=[n==1]
所以显然indd|iμ(d)=1
然后变形

nid|idμ(d)=ndndkμ(k)=ndsum(nd)=1

sum(n)=1d=2dsum(nd)

所以先线性筛出前5000000个数,后面分块就可以了。
ϕ也差不多,利用ii|nϕ(i)=n
code:

#include<iostream>#include<cstdio>#include<algorithm>#include<cmath>#include<map>#include<cstring>#define N 5000000#define LL long long using namespace std;int t;int n,mu[N+30],prime[N+30],v[N+30],pr;LL phi[N+30];map<int,LL> ans,ans1;void pre(){    memset(v,true,sizeof(v));    pr=0;phi[1]=1;mu[1]=1;    for(int i=2;i<=5000000;i++)    {        if(v[i]) prime[++pr]=(LL)i,phi[i]=(LL)(i-1),mu[i]=-1LL;        for(int j=1;j<=pr&&(LL)i*prime[j]<=5000000;j++)        {            v[i*prime[j]]=false;            if(i%prime[j]==0){phi[i*prime[j]]=phi[i]*prime[j];mu[i*prime[j]]=0LL;break;}            phi[i*prime[j]]=phi[i]*(prime[j]-1);mu[i*prime[j]]=-mu[i];        }    }    for(int i=2;i<=5000000;i++) phi[i]+=phi[i-1],mu[i]+=mu[i-1];}LL sumphi(LL n){    if (n<=N) return phi[n];    if (ans[n]) return ans[n];    LL tmp; int j;    if (n&1) tmp=(n+1)/2LL*n;    else tmp=n/2LL*(n+1);    for (int i=2;i<=n;i=j+1)    {        if (n/i) j=min(n,n/(n/i));        else j=n;        tmp-=sumphi(n/i)*(LL)(j-i+1);        if (j==n) break;    }    ans[n]=tmp;    return tmp;}LL summu(LL n){    if (n<=N) return mu[n];    if (ans1[n]) return ans1[n];    LL tmp=1; int j;    for (int i=2;i<=n;i=j+1)    {        if (n/i) j=min(n,n/(n/i));        else j=n;        tmp-=summu(n/i)*(LL)(j-i+1);        if (j==n) break;    }    ans1[n]=tmp;     return tmp;}int main(){    pre();    scanf("%d",&t);ans.clear();ans1.clear();    for (int i=1;i<=t;i++)    {        scanf("%lld",&n);         printf("%lld %lld\n",sumphi(n),summu(n));    }}