今天的數學課上,Crash小朋友學習了最小公倍數(Least Common Multiple)。對於兩個正整數a和b,LCM(a, b)表示能同時被a和b整除的最小正整數。例如,LCM(6, 8) = 24。回到家後,Crash還在想着課上學的東西,爲了研究最小公倍數,他畫了一張NM的表格。每一個格子裏寫了一個數字,其中第i行第j列的那個格子裏寫着數爲LCM(i, j)。一個45的表格以下: 1 2 3 4 5 2 2 6 4 10 3 6 3 12 15 4 4 12 4 20 看着這個表格,Crash想到了不少能夠思考的問題。不過他最想解決的問題倒是一個十分簡單的問題:這個表格中全部數的和是多少。當N和M很大時,Crash就一籌莫展了,所以他找到了聰明的你用程序幫他解決這個問題。因爲最終結果可能會很大,Crash只想知道表格裏全部數的和mod 20101009的值。c++
輸入的第一行包含兩個正整數,分別表示N和M。學習
輸出一個正整數,表示表格中全部數的和mod 20101009的值。spa
4 5code
122
【數據規模和約定】
100%的數據知足N, M ≤ 10^7。ip
莫比烏斯反演裸題。
\[ \large \begin{aligned} &\sum_{i=1}^{n}\sum_{j=1}^{m}lcm(i,j)\\ &=\sum_{d=1}^{n}\sum_{i=1}^{n}\sum_{j=1}^m\frac{ij}{d}[(i,j)=1]\\ &=\sum_{d=1}^{n}\sum_{i=1}^{\frac{n}{d}}\sum_{j=1}^{\frac{m}{d}}ijd[(i,j)=1]\\ &=\sum_{d=1}^{n}\sum_{i=1}^{\frac{n}{d}}\sum_{j=1}^{\frac{m}{d}}ijd\sum_{k|gcd(i,j)}\mu(k)\\ &=\sum_{d=1}^{n}d\sum_{k=1}^{n}\mu(k) k^2\sum_{i=1}^{\frac{n}{kd}}i\sum_{j=1}^{\frac{m}{kd}}j \end{aligned} \]
式子推出來以後兩次數論分塊便可,注意不要溢出就好
複雜度\(O(n)\)input
#include <bits/stdc++.h> using namespace std; #define mod 20101009 typedef long long ll; const int inf = 0x3f3f3f3f; const int N = 1e7 + 10; ll mu[N], p[N], cnt; ll n, m, sum[N]; bool vis[N]; void pre() { ll limit = max(n, m); mu[1] = sum[1] = 1; for(ll i = 2; i <= limit; ++i) { if(!vis[i]) p[++cnt] = i, mu[i] = -1; for(int j = 1; j <= cnt && i * p[j] <= limit; ++j) { vis[i * p[j]] = 1; if(i % p[j] == 0) {mu[i * p[j]] = 0; break;} mu[i * p[j]] = -mu[i]; } sum[i] = sum[i - 1] + mu[i] * i % mod * i % mod; sum[i] %= mod; } } ll sum1(ll x, ll y) { return 1ll * (((x * (x + 1) / 2) % mod) * ((y * (y + 1) / 2) % mod)) % mod; } ll calc(ll a, ll b) { if(a > b) swap(a, b); ll s = 0; for(ll r, l = 1; l <= a; l = r + 1) { r = min(a / (a / l), b / (b / l)); s = (1ll * s + 1ll * (sum[r] - sum[l - 1] + mod) % mod * sum1(a / l, b / l) % mod) % mod; } return s % mod; } int main() { scanf("%lld%lld", &n, &m); pre(); ll ans = 0; if(n > m) swap(n, m); for(ll r, l = 1; l <= n; l = r + 1) { r = min(n / (n / l), m / (m / l)); ans += 1ll * calc(n / l, m / l) % mod * (1ll * (r - l + 1) * (l + r) / 2 % mod) % mod; ans %= mod; } printf("%lld\n", (ans % mod + mod) % mod); return 0; }