BZOJ 2005: [Noi2010]能量采集
二次联通门 : BZOJ 2005: [Noi2010]能量采集
$\LARGE Answer=2*(\sum _{i=1}^{n}\sum_{j=1}^m\gcd(i,j))-nm$
重点是求$\LARGE \sum ^{n}_{i=1}\sum ^{m}_{j=1}\gcd \left( i,j\right)$
由定理 : 一个数等于它约数的欧拉函数之和
即
$\LARGE n=\sum _{d|n}\varphi \left( d\right)$
可知:
$\LARGE \sum ^{n}_{i=1}\sum ^{m}_{j=1}\gcd \left( i,j\right)$
$\LARGE =\sum ^{n}_{i=1}\sum ^{m}_{j=1}\sum _{d|\gcd \left( i,j\right) }\varphi \left( d\right)$
$\LARGE =\sum ^{\max \left( n,m\right) }_{d=1}\varphi \left( d\right) \sum ^{n/d}_{i=1}\sum ^{m/d}_{j=1}1$
$\LARGE =\sum ^{\max \left( n,m\right) }_{d=1}\varphi \left( n\right) \lfloor \dfrac {n}{d}\rfloor \lfloor \dfrac {m}{d}\rfloor$
然后除法分块,对欧拉函数做前缀和即可
/* BZOJ 2005: [Noi2010]能量采集 莫比乌斯反演 */ #include <cstdio> #include <iostream> #include <cmath> #define rg register #define Max 1000005 int p[Max], phi[Max]; bool is[Max]; typedef long long LL; LL s[Max]; void Euler (int N) { int C = 0; phi[1] = 1; rg int i, j; for (i = 2; i <= N; ++ i) { if (!is[i]) p[++ C] = i, phi[i] = i - 1; for (j = 1; j <= C && i * p[j] <= N; ++ j) { is[i * p[j]] = true; if (i % p[j] == 0) phi[i * p[j]] = phi[i] * p[j]; else phi[i * p[j]] = phi[i] * (p[j] - 1); } } for (i = 1; i <= N; ++ i) s[i] = s[i - 1] + phi[i]; } inline int min (int a, int b) { return a < b ? a : b; } int main (int argc, char *argv[]) { int N, M; scanf ("%d%d", &N, &M); rg int i, j; if (N > M) std :: swap (N, M); Euler (N); LL Answer = 0; for (i = 1; i <= N; i = j + 1) { j = min (N / (N / i), M / (M / i)); Answer += (LL) (s[j] - s[i - 1]) * (N / i) * (M / i); } Answer = (LL) Answer * 2 - (LL) N * M; std :: cout << Answer; return 0; }
myj 吊打我Orz,xxy 捆起来打我Orz,myl 文化课上天Orz, lrh 姿势水平敲高Orz, hkd 特别胖Orz%%%,cys 智商感人Orz,syl zz专业Orz,我没有学上, 我们未来一片光明