這個式子正常是 \(\Theta(n)\) 的效率,可是咱們還能夠縮小成 \(\Theta(\sqrt{n})\)。html
能夠發現,對於每一個相同值的一塊,最後一個數是 \(\frac{n}{\frac{n}{i}}\) ,而後就能夠 \(\Theta(\sqrt{n})\) 處理了。c++
for(int l=1,r;l<=n;l=r+1){ r=n/(n/l); ans+=(r-l+1)*(n/l); }
\(\mu (d)\) 爲莫比烏斯函數的函數名,是一個由容斥係數所構成的函數。其定義爲:
一、當 \(d = 1\) 時, \(\mu (d)=1\) 。
二、當 \(d = \prod_{i-1}^k\ p_i\) ,而且全部的 \(p_i\) 爲不一樣的素數的時候, \(\mu(d)=(-1)^k\) 。
三、當 \(d\) 含有的任一質因子的冪大於 \(2\) 次,那麼 \(\mu(d)=0\) 。算法
一、最經常使用:對於任意正整數 \(n\) ,\(\sum_{d|n}\mu(d)=[n=1]\) 。其中 \([\ ]\) 表明的是布爾型,即只有 \(n=1\) 成立的時候才爲 \(1\) ,其餘狀況爲 \(0\) 。(由 \(\mu\) 的容斥係數的性質可證實。PS:我不會。)函數
二、對於任意正整數 \(n\) ,spa
具體的證實見博客:莫比烏斯反演code
任何函數在OI中都是須要用到值的,那麼咱們就須要一些篩法,其中 \(xxs\) (線性篩)是最簡單的一種,在線性篩素數的基礎上稍作修改便可。htm
void xxs(){ mu[1] = 1; for(int i = 2;i <= n;++i){ if(!noprime[i]){ prime[++prime[0]] = i; mu[i] = -1; } for(int j = 1,k;j <= prime[0] && (k = i * prime[j]) <= n;++j){ noprime[k] = 1; if(i % prime[j] == 0)break; else mu[k] = -mu[i]; } } }
有了 \(\mu\) 函數的基礎,咱們就能夠看莫比烏斯繁衍反演了。blog
定義 \(F(n)\) 和 \(f(n)\) 是定義在非負整數集合上的兩個函數,而且知足條件:get
那麼必定存在:博客
此定理即爲莫比烏斯定理。
經過定義證實:
由 \(F(n)\) 和 \(f(n)\) 的定義可得:
那麼
獲得:
莫比烏斯反演的開始。
首先記錄一個套路:在 \(gcd\) 問題中,一般把反演中的 \(f(d)\) 設爲 \(gcd(i,j)=d\)的個數, \(F(n)\) 設爲 \(\sum_{base=1}^{d\times base \leqslant n} [gcd(i,j)=d\times base]\) ,即 \(gcd(i,j)=d\) 和 \(d\) 的倍數的個數。
在這個題中,咱們不這樣定義(由於很差證),這道題要求的是
因此樸素算法就是,咱們能夠枚舉每個質數,進行求和,也就是這樣:
咱們直接把 \(k\) 在後兩個式子中除去,獲得:
而後開始繁衍反演。
因爲只有在 \(gcd(i,j)=1\) 時纔有貢獻,那麼咱們就能夠根據第一個莫比烏斯函數的性質(忘了往上翻)把最後一個 \(gcd(i,j)=1\) 進行轉換,變爲:
那麼在這裏, \(d\) 必定是 \(gcd(i,j)\) 的倍數,因此咱們就能夠繼續化簡,在 \(i\),\(j\) 的極限值上同時除以一個 \(d\) ,直接乘在式子裏便可,而後枚舉 \(d\)。獲得:
這樣的時間複雜度仍是不對的,由於最大的 \(n,m\) 是 \(10^7\),因此咱們就能夠(不得不)繼續化簡。
設 \(tmp=kd\) ,那麼原式子就變成了:
咱們再對這個式子化簡,把 \(tmp\) 提早枚舉,那麼咱們就獲得了最終的式子:
最後這個式子咱們能夠在線性篩的時候用迪利克雷前綴和來搞一個前綴和,而後在下邊只須要用整除分塊的思想枚舉 \(tmp\) ,每一塊的和就能求出來了。
#include<bits/stdc++.h> using namespace std; const int maxn = 1e7+10; int mu[maxn],noprime[maxn],prime[maxn]; int sum[maxn],f[maxn]; int n; void xxs(){ mu[1] = 1; noprime[1] = 1; for(int i = 2;i <= 10000000;++i){ if(!noprime[i]){ prime[++prime[0]] = i; mu[i] = -1; } for(int j = 1,k;j <= prime[0] && (k = i * prime[j]) <= 10000000;++j){ noprime[k] = 1; if(i % prime[j] == 0)break; else mu[k] = -mu[i]; } } for(int i = 1;i <= prime[0];++i){ for(int j = 1;j * prime[i] <= 10000000;++j){ f[j * prime[i]] += mu[j]; } } for(int i = 1;i <= 10000000;++i){ sum[i] = sum[i-1] + f[i]; } } #define ll long long int main(){ xxs(); int T; scanf("%d",&T); while(T--){ int n,m; scanf("%d%d",&n,&m); if(n > m)swap(n,m); long long ans = 0; for(int l = 1,r = 0;l <= n;l = r + 1){ r = min(n / (n / l),m / (m / l)); ans += (ll)(sum[r] - sum[l-1]) * (ll)(n / l) * (ll)(m / l); } printf("%lld\n",ans); } return 0; }
莫比烏斯反演的一些知識就到這裏,其考察的一些題都是一些推柿子,因此須要慢慢鑽研。篩法之後可能會更新(那得看我會不會退役),持續更新(咕咕咕)。
\(Never\ Give\ Up\)