Luogu 3911 最小公倍数之和
感觉自己被早上的名校协作体和下午的数学题虐哭了,每天为自己的菜发愁……
发现$a_{i}$很小,开一个桶记一下每个数 出现的个数,设$c_{i} = \sum_{j = 1}^{n}(a_{j} == i)$。
我们知道$lcm(i, j) == \frac{ij}{gcd(i, j)}$。
记$m = max(a_{i})$。
那么 $\sum_{i = 1}^{n}\sum_{j = 1}^{n}lcm(a_{i}, a_{j}) \ = \sum_{i = 1}^{m}\sum_{j = 1}^{m}\frac{ij}{gcd(i, j)} * c_{i} * c_{j}$。
反演一下,得到 $\sum_{d = 1}^{m}d\sum_{t = 1}^{\left \lfloor \frac{m}{d} \right \rfloor}t^{2} * \mu (t)\sum_{u = 1}^{\left \lfloor \frac{m}{dt} \right \rfloor}\sum_{v = 1}^{\left \lfloor \frac{m}{dt} \right \rfloor}u * v * c_{dtu} * c_{dtv}$。
枚举$T = dt$,得到 $\sum_{T = 1}^{m}(T (\sum_{d | T}d * \mu (d)))(\sum_{u = 1}^{\left \lfloor \frac{m}{T} \right \rfloor}u * c_{Tu})(\sum_{v = 1}^{\left \lfloor \frac{m}{T} \right \rfloor}v * c_{Tv})$。
设$h(T) = \sum_{d | T}d * \mu (d)$,$g(T) = \sum_{d = 1}^{\left \lfloor \frac{m}{T} \right \rfloor}d * c_{Td}$,那么最后的答案就变成了$\sum_{T = 1}^{m}T * h(i) * g^{2}(i)$。
发现$h(i)$是一个积性函数,有$h(1) = 1, h(p) = 1 - p, h(p^{k}) = h(p)$,可以线性筛。
而$g(i)$只要暴力算就可以了。
时间复杂度$O(nlogn)$。
Code:
#include <cstdio> #include <cstring> using namespace std; typedef long long ll; const int N = 5e4 + 5; int n, m = 0, pCnt = 0, pri[N], low[N]; ll h[N], g[N], c[N]; bool np[N]; template <typename T> inline void read(T &X) { X = 0; char ch = 0; T op = 1; for(; ch > '9'|| ch < '0'; ch = getchar()) if(ch == '-') op = -1; for(; ch >= '0' && ch <= '9'; ch = getchar()) X = (X << 3) + (X << 1) + ch - 48; X *= op; } inline void chkMax(int &x, int y) { if(y > x) x = y; } inline void sieve() { h[1] = 1; for(int i = 2; i <= m; i++) { if(!np[i]) { pri[++pCnt] = i; low[i] = i; h[i] = (ll)1 - i; } for(int j = 1; j <= pCnt && i * pri[j] <= m; j++) { np[i * pri[j]] = 1; if(i % pri[j] == 0) { low[i * pri[j]] = low[i] * pri[j]; if(low[i] == i) h[i * pri[j]] = h[i]; else h[i * pri[j]] = h[i / low[i]] * h[pri[j] * low[i]]; break; } low[i * pri[j]] = pri[j]; h[i * pri[j]] = h[i] * h[pri[j]]; } } /* for(int i = 1; i <= m; i++) printf("%lld ", h[i]); printf("\n"); */ } int main() { read(n); for(int i = 1; i <= n; i++) { ll x; read(x); c[x]++; chkMax(m, x); } /* for(int i = 1; i <= m; i++) printf("%lld ", c[i]); printf("\n"); */ sieve(); for(int i = 1; i <= m; i++) for(int j = 1; j <= m / i; j++) g[i] += c[i * j] * j; /* for(int i = 1; i <= m; i++) printf("%lld ", g[i]); printf("\n"); */ ll ans = 0LL; for(int i = 1; i <= m; i++) ans += i * h[i] * g[i] * g[i]; printf("%lld\n", ans); return 0; }