BZOJ 3944 - Sum(杜教筛)

题目

戳这里

子问题:

     ∑ i = 1 n φ ( i ) ~~~~\sum_{i=1}^{n}\varphi(i)     i=1nφ(i)

因为

     ∑ i ∣ n φ ( i ) = n ~~~~\sum_{i|n}\varphi(i)=n     inφ(i)=n

所以

     φ ( n ) = n − ∑ i ∣ n , i &lt; n φ ( i ) ~~~~\varphi(n)=n-\sum_{i|n,i&lt;n}\varphi(i)     φ(n)=nin,i<nφ(i)

辣么

     ∑ i = 1 n φ ( i ) ~~~~\sum_{i=1}^{n}\varphi(i)     i=1nφ(i)
= ∑ i = 1 n ( i − ∑ d ∣ i , d &lt; i φ ( d ) ) =\sum_{i=1}^{n}(i-\sum_{d|i,d&lt;i}\varphi(d)) =i=1n(idi,d<iφ(d))
= n ( n + 1 ) 2 − ∑ i = 2 n ∑ d ∣ i , d &lt; i φ ( d ) =\frac{n(n+1)}{2}-\sum_{i=2}^{n}\sum_{d|i,d&lt;i}\varphi(d) =2n(n+1)i=2ndi,d<iφ(d)
= n ( n + 1 ) 2 − ∑ i = 2 n ∑ d = 1 ⌊ n i ⌋ φ ( d ) =\frac{n(n+1)}{2}-\sum_{i=2}^{n}\sum_{d=1}^{\lfloor\frac{n}{i}\rfloor}\varphi(d) =2n(n+1)i=2nd=1inφ(d)

同理
子问题:

     ∑ i = 1 n μ ( i ) ~~~~\sum_{i=1}^{n}\mu(i)     i=1nμ(i)

因为

     ∑ i ∣ n μ ( i ) = [ i = = 1 ] ~~~~\sum_{i|n}\mu(i)=[i==1]     inμ(i)=[i==1]

所以

     φ ( n ) = [ n = = 1 ] − ∑ i ∣ n , i &lt; n φ ( i ) ~~~~\varphi(n)=[n==1]-\sum_{i|n,i&lt;n}\varphi(i)     φ(n)=[n==1]in,i<nφ(i)

辣么

     ∑ i = 1 n μ ( i ) ~~~~\sum_{i=1}^{n}\mu(i)     i=1nμ(i)
= ∑ i = 1 n ( [ i = = 1 ] − ∑ d ∣ i , d &lt; i μ ( d ) ) =\sum_{i=1}^{n}([i==1]-\sum_{d|i,d&lt;i}\mu(d)) =i=1n([i==1]di,d<iμ(d))
= 1 − ∑ i = 2 n ∑ d ∣ i , d &lt; i μ ( d ) =1-\sum_{i=2}^{n}\sum_{d|i,d&lt;i}\mu(d) =1i=2ndi,d<iμ(d)
= 1 − ∑ i = 2 n ∑ d = 1 ⌊ n i ⌋ μ ( d ) =1-\sum_{i=2}^{n}\sum_{d=1}^{\lfloor\frac{n}{i}\rfloor}\mu(d) =1i=2nd=1inμ(d)

两个子问题都可以用杜教筛 O ( n 2 3 ) O(n^{\frac{2}{3}}) O(n32)下求解

代码:

#include<cstdio>
#include<cmath>
#include<cstring>
#include<map>
#include<vector>
#include<iostream>
#include<algorithm>
 
#define maxn 5000000
 
using namespace std;
 
inline int getint()
{
    int num=0,flag=1;char c;
    while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
    while(c>='0'&&c<='9')num=num*10+c-48,c=getchar();
    return num*flag;
}

map<long long,long long>Mu,Phi;
bool not_prime[maxn+5];
long long phi[maxn+5],mu[maxn+5],prime[maxn+5],cnt;
long long n;
 
inline void init()
{
    mu[1]=phi[1]=1;
    for(int i=2;i<=maxn;i++)
    {
        if(!not_prime[i])prime[++cnt]=i,mu[i]=-1,phi[i]=i-1;
        for(int j=1;j<=cnt&&i*prime[j]<=maxn;j++)
        {
            not_prime[i*prime[j]]=1;
            if(i%prime[j])mu[i*prime[j]]=-mu[i],phi[i*prime[j]]=phi[i]*phi[prime[j]];
            else{phi[i*prime[j]]=phi[i]*prime[j];break;}
        }
    }
    for(int i=1;i<=maxn;i++)phi[i]+=phi[i-1],mu[i]+=mu[i-1];
}
 
inline long long solvephi(long long x)
{
    if(x<=maxn)return phi[x];
    if(Phi.count(x))return Phi[x];
    long long Sum=1ll*x*(1ll*x+1)/2;
    for(long long i=2,j;i<=x;i=j+1)
        j=min(x/(x/i),x),Sum-=1ll*(j-i+1)*solvephi(x/i);
    return Phi[x]=Sum;
}
 
inline long long solvemu(long long x)
{
    if(x<=maxn)return mu[x];
    if(Mu.count(x))return Mu[x];
    long long Sum=1;
    for(long long i=2,j;i<=x;i=j+1)
        j=min(x/(x/i),x),Sum-=1ll*(j-i+1)*solvemu(x/i);
    return Mu[x]=Sum;
}
 
int main()
{
    init();
    int T=getint();
    while(T--)
    {
        n=getint();
        printf("%lld %lld\n",solvephi(n),solvemu(n));
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值