二次联通门 : BZOJ 2005: [Noi2010]能量采集
$LARGE Answer=2*(sum _{i=1}^{n}sum_{j=1}^mgcd(i,j))-nm$
重点是求$LARGE sum ^{n}_{i=1}sum ^{m}_{j=1}gcd left( i,j ight)$
由定理 : 一个数等于它约数的欧拉函数之和
即
$LARGE n=sum _{d|n}varphi left( d
ight)$
可知:
$LARGE sum ^{n}_{i=1}sum ^{m}_{j=1}gcd left( i,j
ight)$
$LARGE =sum ^{n}_{i=1}sum ^{m}_{j=1}sum _{d|gcd left( i,j
ight) }varphi left( d
ight)$
$LARGE =sum ^{max left( n,m
ight) }_{d=1}varphi left( d
ight) sum ^{n/d}_{i=1}sum ^{m/d}_{j=1}1$
$LARGE =sum ^{max left( n,m
ight) }_{d=1}varphi left( n
ight) lfloor dfrac {n}{d}
floor lfloor dfrac {m}{d}
floor$
然后除法分块,对欧拉函数做前缀和即可
/* 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; }