1. 程式人生 > >[51NOD1237]最大公約數之和 V3

[51NOD1237]最大公約數之和 V3

題目大意

給定n,試求

i=1nj=1ngcd(i,j)
結果對109+7取模。

2n1010

題目分析

我們將題目改為求

i=1nj=1igcd(i,j)
然後將答案乘二再減去1n的和即可。
那麼上面這條式子是什麼呢?
i=1nj=1igcd(i,j)=d=1ndi=1ndφ(i)=d=1nφ(d)(1+nd)nd2
nd分塊,對前面的尤拉函式使用杜教篩求字首和。
時間複雜度?這個算起來有點麻煩,我來口胡一下。
假設我們預處理的部分大小為a
i=1nj=1iaOij+i=1nj=1nia
O(nij)

顯然右邊部分的複雜度要大於左邊,使用積分來計算:
i=1nj=1niaO(nij)O(n0(nax0nxydy)dx)=O(n0nx1a)=O(1anlogn)
然後a可以在空間限制內儘量取大一點,我取了1.5×107

程式碼實現

我的程式碼莫名其妙常數巨大,卡著空間取的a讓我卡著時限過了這題。。。

#include <iostream>
#include <cstdio>
#include <cmath>

using namespace std;

typedef long long LL;

const int
P=1000000007; const int itwo=500000004; const int L=15000000; const int N=100050; int pri[L+5],phi[L+5],f[L+5],sum[L+5]; int S[N],vis[N]; bool mark[L+5]; int ans,l,s,cnt; LL n; void pre() { s=trunc(sqrt(n)),l=L; mark[1]=1,sum[1]=phi[1]=1,f[1]=1; for (int i=2;i<=l;++i) { if (!mark[i]) pri[++pri[0
]]=i,phi[i]=i-1,f[i]=i; for (int j=1,k;j<=pri[0];++j) { i