for i=1 to nc++
for j=1 to n函數
sum+=gcd(i,j)
給出n求sum. gcd(x,y)表示x,y的最大公約數.spa
輸入格式:code
nget
輸出格式:it
sumio
輸入樣例#1:class
2gc
輸出樣例#1:im
5
數據範圍 30% n<=3000 60% 7000<=n<=7100 100% n<=100000
這道題的作法貌似不少...若是你同時會狄利克雷卷積和莫比烏斯反演的話也能夠強行反演一波,反正蒟蒻我是不會卷的,因此在這裏介紹另一種作法
一個式子描述題意\(ans=\sum _{i=1}^{n}\sum_{j=1}^{n}gcd(i,j)\)
直接暴力確定是不行的,咱們想一下有沒有辦法求出一個數它做爲\(gcd\)的貢獻呢?
對於兩個數\(gcd(a,b)=1\to gcd(ka,kb)=k(ka<=n,kb<=n)\),因此k做爲\(gcd\)的貢獻就是\(gcd(x,y)=k\)的數對的對數,還不許確,由於數對\((x,y),(y,x)\),分別對答案都有貢獻,但x=y的狀況只能算一次,因此是 **數對的個數*2-1**,那麼關鍵就在於怎麼快速算出這個對數
咱們發現\(n\)之內\(gcd\)爲\(k\)的對數,實際上就是\(\lfloor\frac{n}{k}\rfloor\)之內gcd爲1的數對的對數,這其實就是\(\lfloor\frac{n}{k}\rfloor\)之內每一個數的歐拉函數的值之和,即\(2\times \sum_{i=1}^{\lfloor\frac{n}{k}\rfloor}\phi(i)-1\),這個對數*數值就是每一個數的貢獻
線性篩一遍歐拉函數求前綴和就能夠了....
#include<bits/stdc++.h> #define in(i) (i=read()) #define il extern inline #define rg register #define mid ((l+r)>>1) #define Min(a,b) ((a)<(b)?(a):(b)) #define Max(a,b) ((a)>(b)?(a):(b)) #define lol long long using namespace std; const lol N=1e5+10; lol read() { lol ans=0, f=1; char i=getchar(); while (i<'0' || i>'9') {if(i=='-') f=-1; i=getchar();} while (i>='0' && i<='9') ans=(ans<<1)+(ans<<3)+(i^48), i=getchar(); return ans*f; } lol n,ans,cnt,vis[N],prime[N],phi[N]={0,1}; void init() { for (lol i=2;i<=N-10;i++) { if (!vis[i]) prime[++cnt]=i,phi[i]=i-1; for (lol j=1;j<=cnt && prime[j]*i<=N-10;j++) { vis[i*prime[j]]=1; if(i%prime[j]==0) {phi[i*prime[j]]=phi[i]*prime[j]; break;} else phi[i*prime[j]]=phi[i]*(prime[j]-1); } }for (lol i=1;i<=N-10;i++) phi[i]+=phi[i-1]; } int main() { in(n); init(); for (lol i=1;i<=n;i++) ans+=(2*phi[n/i]-1)*i; cout<<ans<<endl; }