【HYSBZ-2005】能量采集 (莫比乌斯反演 + 分块)

能量采集 HYSBZ - 2005

栋栋有一块长方形的地,他在地上种了一种能量植物,这种植物可以采集太阳光的能量。在这些植物采集能量后,栋栋再使用一个能量汇集机器把这些植物采集到的能量汇集到一起。 栋栋的植物种得非常整齐,一共有 n n 列,每列有 m m 棵,植物的横竖间距都一样,因此对于每一棵植物,栋栋可以用一个坐标 ( x , y ) (x, y) 来表示,其中 x x 的范围是 1 1 n n ,表示是在第 x x 列, y y 的范围是 1 1 m m ,表示是在第 x x 列的第 y y 棵。 由于能量汇集机器较大,不便移动,栋栋将它放在了一个角上,坐标正好是 ( 0 , 0 ) (0, 0) 。 能量汇集机器在汇集的过程中有一定的能量损失。如果一棵植物与能量汇集机器连接而成的线段上有 k k 棵植物,则能量的损失为 2 k + 1 2k + 1 。例如,当能量汇集机器收集坐标为 ( 2 , 4 ) (2, 4) 的植物时,由于连接线段上存在一棵植物 ( 1 , 2 ) (1, 2) ,会产生 3 3 的能量损失。注意,如果一棵植物与能量汇集机器连接的线段上没有植物,则能量损失为 1 1 。现在要计算总的能量损失。 下面给出了一个能量采集的例子,其中 n = 5 m = 4 n = 5,m = 4 ,一共有 20 20 棵植物,在每棵植物上标明了能量汇集机器收集它的能量时产生的能量损失。 在这个例子中,总共产生了 36 36 的能量损失。

Input

仅包含一行,为两个整数 n n m m

Output

仅包含一个整数,表示总共产生的能量损失。

Sample Input

5 4
3 4

Sample Output

36
20

Hint

对于100%的数据:1 ≤ n, m ≤ 100,000。

题解

有题可知 k = g c d ( x , y ) 1 k=gcd(x,y) - 1 ,因此 2 k 1 = 2 g c d ( x , y ) 1 2k-1 =2 * gcd(x,y)-1
题目所求可以表示为
x = 1 n y = 1 m ( 2 g c d ( x , y ) 1 ) = 2 x = 1 n y = 1 m g c d ( x , y ) n m \begin{aligned} &\sum_{x = 1}^{n}\sum_{y=1}^{m}(2*gcd(x,y)-1)\\ =&2\sum_{x = 1}^{n}\sum_{y=1}^{m}gcd(x,y)- n*m\\ \end{aligned}
现在我们尝试单独观察 x = 1 n y = 1 m g c d ( x , y ) \sum_{x = 1}^{n}\sum_{y=1}^{m}gcd(x,y)
x = 1 n y = 1 m g c d ( x , y ) = x = 1 n y = 1 m k = 1 m i n ( n , m ) k [ g c d ( x , y ) = k ] = k = 1 m i n ( n , m ) k x = 1 n y = 1 m [ g c d ( x , y ) = k ] = k = 1 m i n ( n , m ) k x = 1 n k y = 1 m k [ g c d ( x , y ) = 1 ] = k = 1 m i n ( n , m ) k x = 1 n k y = 1 m k d g c d ( x , y ) μ ( d ) = k = 1 m i n ( n , m ) k d = 1 m i n ( n k , m k ) μ ( d ) n k d m k d \begin{aligned} &\sum_{x = 1}^{n}\sum_{y=1}^{m}gcd(x,y)\\ =&\sum_{x = 1}^{n}\sum_{y=1}^{m}\sum_{k=1}^{min(n,m)}k*[gcd(x,y)=k]\\ =&\sum_{k=1}^{min(n,m)}k\sum_{x = 1}^{n}\sum_{y=1}^{m}[gcd(x,y)=k]\\ =&\sum_{k=1}^{min(n,m)}k\sum_{x = 1}^{\lfloor{n\over k}\rfloor}\sum_{y=1}^{\lfloor{m\over k}\rfloor}[gcd(x,y)=1]\\ =&\sum_{k=1}^{min(n,m)}k\sum_{x = 1}^{\lfloor{n\over k}\rfloor}\sum_{y=1}^{\lfloor{m\over k}\rfloor}\sum_{d|gcd(x,y)}\mu(d)\\ =&\sum_{k=1}^{min(n,m)}k\sum_{d=1}^{min(\lfloor{n\over k}\rfloor,\lfloor{m\over k}\rfloor)}\mu(d)*\lfloor{n\over kd}\rfloor*\lfloor{m\over kd}\rfloor\\ \end{aligned}
此时结果已经出来了。
a n s = 2 k = 1 m i n ( n , m ) k d = 1 m i n ( n k , m k ) μ ( d ) n k d m k d n m ans=2*\sum_{k=1}^{min(n,m)}k\sum_{d=1}^{min(\lfloor{n\over k}\rfloor,\lfloor{m\over k}\rfloor)}\mu(d)*\lfloor{n\over kd}\rfloor*\lfloor{m\over kd}\rfloor-n*m

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 1e5 + 10;
int mu[maxn], prime[maxn], cnt = 0;
bool vis[maxn];

void init() {
  mu[1] = 1;
  for (int i = 2; i < maxn; i++) {
    if (vis[i] == false) {
      prime[cnt++] = i;
      mu[i] = -1;
    }
    for (int j = 0; j < cnt && i * prime[j] < maxn; j++) {
      vis[i * prime[j]] = true;
      if (i % prime[j] == 0) {
        mu[i * prime[j]] = 0;
        break;
      } else {
        mu[i * prime[j]] = -mu[i];
      }
    }
  }
  for (int i = 2; i < maxn; i++) {
    mu[i] += mu[i - 1];
  }
}

LL solve(int n, int m) {
  LL ans = 0;
  if (n > m) swap(n, m);
  for (int i = 1; i <= n; i++) {
    LL tmp = 0;
    int a = n/i, b = m/i;
    for (int l = 1, r; l <= a; l = r + 1) {
      r = min(a/(a/l), b/(b/l));
      tmp += (LL)(mu[r] - mu[l - 1]) * (a/l) * (b/l);
    }
    ans += tmp * i;
  }
  return ans;
}

int main()
{
  init();
  int n, m;
  while (scanf("%d%d", &n, &m) != EOF) {
    printf("%lld\n", 2 * solve(n, m) - 1LL * n * m);
  }

  return 0;
}

发布了79 篇原创文章 · 获赞 56 · 访问量 50万+

猜你喜欢

转载自blog.csdn.net/qq_40861916/article/details/90683924
今日推荐