bzoj3944sum

来源:互联网 发布:淘宝橙色cmyk 编辑:程序博客网 时间:2024/06/06 02:18

传送门
杜教筛
可以知道ni=1d|iφ(d)==n(n+1)/2
把式子转化成ni=1n/id=1φ(d)
这样的话每个φ(d)都被算了多次
容斥一下
式子变为n(n+1)/xni=2n/id=1φ(d)
可以看出后面其实是一个规模较小的问题
直接上杜教筛

#include <iostream>#include <cstdio>#include <cstring>using namespace std;const int maxn=2000000+10;typedef long long ll;ll phi[maxn];ll mu[maxn];ll prime[maxn];ll ps=0;ll q[maxn],p[maxn];int vis[maxn];ll m=2000000;ll n;int done[maxn];inline void get_prime(){    mu[1]=1;    phi[1]=1;    for(int i=2;i<=m;i++){        if(!vis[i]){            prime[++ps]=i;            mu[i]=-1;            phi[i]=i-1;        }        for(int j=1;j<=ps;j++){            if(prime[j]*i>m)                break;            vis[i*prime[j]]=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(int i=2;i<=m;i++)        mu[i]+=mu[i-1],phi[i]+=phi[i-1];}inline ll get_phi(ll x){    if(x<=m)        return phi[x];    return q[n/x];}inline ll get_mu(ll x){    if(x<=m)        return mu[x];    return p[n/x];}inline void work(ll x){    if(x<=m)        return ;    //printf("%lld\n",x);    ll t=n/x;    if(done[t])        return ;    done[t]=1;    ll nw=2;    p[t]=1,q[t]=(x*(x+1ll)/2);    while(nw<=x){        ll tmp=x/nw;        ll r=x/tmp;        work(tmp);        p[t]-=get_mu(tmp)*(r-nw+1);q[t]-=get_phi(tmp)*(r-nw+1);        nw=r+1;    }}int main(){    //freopen("a.in","r",stdin);    //freopen("a.out","w",stdout);    ll T;    scanf("%lld",&T);    get_prime();    while(T--){        scanf("%lld",&n);        memset(done,0,sizeof(done));        if(n<=m)            printf("%lld %lld\n",phi[n],mu[n]);        else {            work(n);            printf("%lld %lld\n",q[1],p[1]);        }    }return 0;}
原创粉丝点击