Description
栋栋有一块长方形的地,他在地上种了一种能量植物,这种植物可以采集太阳光的能量。在这些植物采集能量后,栋栋再使用一个能量汇集机器把这些植物采集到的能量汇集到一起。 栋栋的植物种得非常整齐,一共有(n)列,每列有(m)棵,植物的横竖间距都一样,因此对于每一棵植物,栋栋可以用一个坐标((x,y))来表示,其中(x)的范围是(1)至(n),表示是在第(x)列,(y)的范围是(1)至(m),表示是在第(x)列的第(y)棵。 由于能量汇集机器较大,不便移动,栋栋将它放在了一个角上,坐标正好是((0,0))。能量汇集机器在汇集的过程中有一定的能量损失。如果一棵植物与能量汇集机器连接而成的线段上有k棵植物,则能量的损失为(2k+1)。例如,当能量汇集机器收集坐标为((2,4))的植物时,由于连接线段上存在一棵植物((1,2)),会产生(3)的能量损失。注意,如果一棵植物与能量汇集机器连接的线段上没有植物,则能量损失为1。现在要计算总的能量损失。 下面给出了一个能量采集的例子,其中(n=5,m=4),一共有(20)棵植物,在每棵植物上标明了能量汇集机器收集它的能量时产生的能量损失。在这个例子中,总共产生了36的能量损失。
Input
仅包含一行,为两个整数(n)和(m)。
Output
仅包含一个整数,表示总共产生的能量损失。
Sample Input
5 4
Sample Output
36
HINT
对于(10\%)的数据:(1 le n,m le 10);
对于(50\%)的数据:(1 le n, m le 100);
对于(80\%)的数据:(1 le n, m le 1000);
对于(90\%)的数据:(1 le n, m le 10000);
对于(100\%)的数据:(1 le n, m le 100000)。
题目求$$sum_{i=1}^{N} sum_{j=1}^{M}2 imes gcd(i,j) - 1$$
转换变成枚举(gcd)求$$sum_{g=1}^{min(N,M)}(2 imes g -1)sum_{i = 1}^{N} sum_{j = 1}^{M} [gcd(i,j) = g]$$
然后同BZOJ 2301 Problem b的做法进行莫比乌斯反演,就可以化成$$sum_{g=1}{min(N,M)}sum_{k=1}{min(lfloor frac{N}{g}
floor,lfloor frac{M}{g}
floor)} mu(k) lfloor frac{N}{kg}
floor lfloor frac{M}{kg}
floor $$
由于$$sum_{i=1}^{N} frac{1}{i} approx ln N$$
所以直接求这个式子复杂度是(O(NlogN))。
由于人比较愚钝,所以我预处理了(mu)的前缀和,还傻逼的(sqrt{N})分段了。
#include<iostream>
#include<cstdio>
#include<cstdlib>
using namespace std;
typedef long long ll;
#define maxn 100010
int n,m,tot,prime[maxn],mu[maxn];
bool exist[maxn]; ll ans;
inline void find()
{
mu[1] = 1;
for (int i = 2;i <= n;++i)
{
if (!exist[i]) prime[++tot] = i,mu[i] = -1;
for (int j = 1;j <= tot&&i*prime[j]<=n;++j)
{
exist[i*prime[j]] = true;
if (i % prime[j] == 0) { mu[i*prime[j]] = 0; break; }
mu[i*prime[j]] = -mu[i];
}
}
for (int i = 1;i <= n;++i) mu[i] += mu[i-1];
}
inline ll calc(int a,int b,int d)
{
a /= d; b /= d;
ll ret = 0; int pos;
for (int i = 1;i <= a;i = pos+1)
{
pos = min(a/(a/i),b/(b/i));
ret += (ll)(mu[pos]-mu[i-1])*(ll)(a/i)*(ll)(b/i);
}
return ret;
}
int main()
{
freopen("2005.in","r",stdin);
freopen("2005.out","w",stdout);
scanf("%d %d",&n,&m); if (n > m) swap(n,m);
find();
for (int i = 1;i <= n;++i)
ans += (ll)((i<<1)-1)*calc(n,m,i);
printf("%lld",ans);
fclose(stdin); fclose(stdout);
return 0;
}