提供兩種作法。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 }