[BZOJ 3944]Sum:杜教篩

點擊這裏查看原題

貼一個杜教篩教程http://blog.csdn.net/skywalkert/article/details/50500009

預處理n^(2/3)+記憶化

注意雖然n的範圍是2^31-1,沒有爆int,但是n+1爆了,所以要用ll

/*
User:Small
Language:C++
Problem No.:2301
*/
#include<bits/stdc++.h>
#define ll long long
#define inf 999999999
using namespace std;
const int M=5e6;
int cnt,prime[M];
bool not_prime[M];
map<int,ll> _phi,_mu;
ll phi[M],mu[M];
ll calphi(ll n){
    if(n<M) return phi[n];
    map<int,ll>::iterator it;
    if((it=_phi.find(n))!=_phi.end()) return it->second;
    ll res=(ll)n*(n+1)/2;
    for(ll i=2,r;i<=n;i=r+1){
        r=n/(n/i);
        res-=(r-i+1)*calphi(n/i);
    }
    return _phi[n]=res;
}
ll calmu(ll n){
    if(n<M) return mu[n];
    map<int,ll>::iterator it;
    if((it=_mu.find(n))!=_mu.end()) return it->second;
    ll res=1;
    for(ll i=2,r;i<=n;i=r+1){
        r=n/(n/i);
        res-=(r-i+1)*calmu(n/i);
    }
    return _mu[n]=res;
}
void solve(){
    int n;
    scanf("%d",&n);
    printf("%lld %lld\n",calphi(n),calmu(n));
}
int main(){
    freopen("data.in","r",stdin);//
    phi[1]=mu[1]=1;
    for(int i=2;i<M;i++){
        if(!not_prime[i]){
            prime[++cnt]=i;
            mu[i]=-1;
            phi[i]=i-1;
        }
        for(int j=1;j<=cnt&&i*prime[j]<M;j++){
            not_prime[i*prime[j]]=1;
            if(i%prime[j]==0){
                phi[i*prime[j]]=phi[i]*prime[j];
                mu[i*prime[j]]=0;
                break;
            }
            phi[i*prime[j]]=phi[i]*phi[prime[j]];
            mu[i*prime[j]]=-mu[i];
        }
    }
    for(int i=1;i<M;i++){
        phi[i]+=phi[i-1];
        mu[i]+=mu[i-1];
    }
    int t;
    scanf("%d",&t);
    while(t--) solve();
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章