HDU 6134 Battlestation Operational

题目大意:求$f(n)=\sum_{i=1}^n\sum_{j=1}^i \lceil \frac{i}{j} \rceil [(i,j)=1]$ 。

莫比乌斯反演

首先,有一个据说很著名的等式:
$$
\sum_{i=1}^n \lfloor \frac{n}{i} \rfloor =\sum_{i=1}^n \sigma(i)
$$
其中$\sigma(n)$ 为$n$ 的因数个数。这个等式可以由筛法证明(见代码)。

然后我们来化简下题目中所要求的函数$f(n)$ 。
$$
f(n)=\sum_{i=1}^n \sum_{j=1}^i \lceil \frac{i}{j} \rceil [(i,j)=1]\\
=\sum_{i=1}^n(\sum_{j=1}^i \lfloor \frac{i}{j} \rfloor[(i,j)=1] +\varphi(i)-1)\\
=\sum_{i=1}^n\sum_{j=1}^i \lfloor \frac{i}{j} \rfloor[(i,j)=1] +\sum_{i=1}^n\varphi(i)-n\\
$$
设$g(n)=\sum_{i=1}^n\sum_{j=1}^i \lfloor \frac{i}{j} \rfloor[(i,j)=1],i=k_1d,j=k_2d$ ,则有
$$
g(n)=\sum_{i=1}^n\sum_{j=1}^i \lfloor \frac{i}{j} \rfloor[(i,j)=1]\\
=\sum_{i=1}^n\sum_{j=1}^i \lfloor \frac{i}{j} \rfloor \sum_{d|(i,j)}u(d)\\
=\sum_{d=1}^nu(d)\sum_{k_1=1}^{\lfloor \frac{n}{d} \rfloor}\sum_{k_2=1}^{k_1}\lfloor \frac{k_1}{k_2} \rfloor\\
=\sum_{d=1}^nu(d)\sum_{k_1=1}^{\lfloor \frac{n}{d} \rfloor}\sum_{k_2=1}^{k_1}\sigma(k_2)
$$
$h(n)=\sum_{i=1}^n\sum_{j=1}^{i}\sigma(j)$ 预处理的复杂度为$O(nlogn)$ 。

于是$f(n)=\sum_{d=1}^nu(d)h(\lfloor \frac{n}{d} \rfloor)+\sum_{i=1}^n\varphi(i)-n$ 可以在$O(\sqrt{n})$ 的复杂度求出。

代码如下:

#include <cstdio>
using namespace std;
typedef long long ll;
const int N=1000001;
const ll p=1e9+7;
ll d[N],h[N],sumd,n;
ll prime[N],vis[N]={1,1},k,phi[N]={0,1},pphi[N],mu[N]={0,1},pmu[N];
ll Max(ll a,ll b){
    return a>b?a:b;
}
void init(){
    for(int i=2;i<N;++i){
        if(!vis[i]){
            prime[k++]=i;
            phi[i]=i-1;
            mu[i]=-1;
        }
        for(int j=0;j<k&&prime[j]*i<N;++j){
            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=1;i<N;++i)
        for(int j=i;j<N;j+=i)
            d[j]++;//预处理\sigma(n)
    for(int i=1;i<N;++i){
        sumd=(sumd+d[i])%p;
        h[i]=(h[i-1]+sumd)%p;
    }
    for(int i=1;i<N;++i){
        pphi[i]=(pphi[i-1]+phi[i])%p;
        pmu[i]=pmu[i-1]+mu[i];
    }
}
ll solve(ll n){
    ll ans=((pphi[n]-n)%p+p)%p;
    for(ll d=1;d<=n;){
        ll pd=Max(d,n/(n/d));
        ans+=(pmu[pd]-pmu[d-1])*h[n/d]%p;
        ans=(ans+p)%p;
        d=pd+1;
    }
    return ans;
}
int main(void){
    init();
    while(~scanf("%I64d",&n))
        printf("%I64d\n",solve(n));
}