提供两种作法。ide
1.简单容斥。spa
这题乍一看有点像[SDOI2008]仪仗队...3d
而后咱们就想到可能跟gcd有关,而后发现点(i,j)的贡献是gcd(i,j) * 2 - 1code
证实以下:blog
设g = gcd(i,j)get
则点(i,j)与原点连线中最靠近原点的点是it
由于此点的横纵坐标互质。io
那么在连线上的全部点就是p, 2p, 3p...(g - 1)pevent
共有(g - 1)个点,因此是2g - 1class
稍微转化一下,咱们枚举g,求有多少对i,j知足gcd(i,j) = g,记为sum[g]
咱们发现仍是不会......
咱们又发现g|gcd(i,j)的数对很好求,是
这样咱们只要用这个值减去sum[2g],sum[3g]....便可。
倒序枚举g便可。
long long大法好。
1 #include <cstdio> 2 #include <algorithm> 3 typedef long long LL; 4 const int N = 100010; 5 6 LL sum[N]; 7 8 int main() { 9 int m, n; 10 LL ans = 0; 11 scanf("%d%d", &n, &m); 12 for(int i = std::min(m, n); i >= 1; i--) { 13 sum[i] = 1ll * (m / i) * (n / i); 14 for(int j = i << 1; j <= std::min(m, n); j += i) { 15 sum[i] -= sum[j]; 16 } 17 //printf("sum[%d] = %lld \n", i, sum[i]); 18 ans += sum[i] * (i * 2 - 1); 19 } 20 printf("%lld", ans); 21 return 0; 22 }
2.莫比乌斯反演。
设
则
且 ans = 2 * ∑i * f(i) - (m * n)
运用莫比乌斯反演以后的最终求和式:
1 #include <cstdio> 2 typedef long long LL; 3 const int N = 100010; 4 5 int n, m, c; 6 int p[N], top, miu[N]; 7 LL f[N]; 8 bool vis[N]; 9 10 inline void getmiu(int b) { 11 miu[1] = 1; 12 for(int i = 2; i <= b; i++) { 13 if(!vis[i]) { 14 p[++top] = i; 15 miu[i] = -1; 16 } 17 for(int j = 1; j <= top && i * p[j] <= b; j++) { 18 vis[i * p[j]] = 1; 19 if(i % p[j] == 0) { 20 break; 21 } 22 miu[i * p[j]] = -miu[i]; 23 } 24 } 25 return; 26 } 27 28 inline LL F(int x) { 29 if(f[x]) { 30 return f[x]; 31 } 32 return f[x] = 1ll * (m / x) * (n / x); 33 } 34 35 inline LL solve(int d) { 36 LL ans = 0; 37 for(int i = 1; i * d <= c; i++) { 38 ans += F(i * d) * miu[i]; 39 } 40 return ans * d; 41 } 42 43 int main() { 44 LL ans = 0; 45 scanf("%d%d", &n, &m); 46 c = n > m ? m : n; 47 getmiu(c); 48 for(int i = 1; i <= c; i++) { 49 ans += solve(i); 50 } 51 printf("%lld", 2 * ans - 1ll * m * n); 52 return 0; 53 }