Victoryztk有一个长方形的矿场,矿物排列得非常整齐,一共有n列,每列有m块,矿石的横竖间距都一样,因此对于每一棵矿石,Victoryztk可以用一个坐标(x, y)来表示,其中x的范围是1至n,表示是在第x列,y的范围是1至m,表示是在第x列的第y块。
由于检查矿物的机器较大,不便移动,Victoryztk将它放在了一个角上,坐标正好是(0, 0)。矿物检查机器在检查的过程中有一定的能量损失。如果一块矿石与矿物检查机器连接而成的线段上有k块矿石,则机器磨损为2k + 1。
例如,当矿物检查机器检查坐标为(2, 4)的矿物时,由于连接线段上存在一块矿石(1, 2),会产生3的机器磨损。注意,如果一块矿石与矿物检查机器连接的线段上没有矿石,则机器磨损为1。现在要计算总的机器磨损。
下面给出了一个机器磨损的例子,其中n = 5,m = 4,一共有20块矿石,在每块矿石上标明了矿物检查机器检查它时产生的机器磨损。 在这个例子中,总共产生了36的能量损失。
Input
仅包含一行,为两个整数n和m。
Output
仅包含一个整数,表示总共产生的能量损失。
Sample Input
【样例输入1】
5 4
【样例输入2】
3 4
Sample Output
【样例输出1】
36
【样例输出2】
20
对于100%的数据:1 ≤ n, m ≤ 100,000。
Hint
15*1+3*3+5*1+7*1=36
红书上的Mobius函数居然是o(nlogn)的。。。跑了4秒多,弄了个线性筛600ms
https://blog.csdn.net/coldfresh/article/details/77099943
这个把它弄到o(n)了 60ms
#include<bits/stdc++.h>
using namespace std;
const int n=1<<20;
int mu[n];
void getMu()
{
memset(mu,0,sizeof(mu));
for(int i=1; i<=n; i++)
{
int target=i==1?1:0;
int delta=target-mu[i];
mu[i]=delta;
for(int j=i+i; j<=n; j+=i)
mu[j]+=delta;
}
}
const int MAXN=1e6+5;
typedef long long LL;
bool check[MAXN+10];
long long prime[MAXN+10];
void Moblus()
{
memset(check,false,sizeof(check));
mu[1] = 1;
long long tot = 0;
for(long long i = 2; i <= MAXN; i++)
{
if( !check[i] )
{
prime[tot++] = i;
mu[i] = -1;
}
for(long long j = 0; j < tot; j++)
{
if(i * prime[j] > MAXN) break;
check[i * prime[j]] = true;
if( i % prime[j] == 0)
{
mu[i * prime[j]] = 0;
break;
}
else
{
mu[i * prime[j]] = -mu[i];
}
}
}
}
long long f[n];
int main()
{
int a,b;
Moblus();
while(scanf("%d%d",&a,&b)!=EOF)
{
long long ans=0;
int c=min(a,b);
for(int i=1; i<=n; i++)
f[i]=1ll*(a/i)*(b/i);
for(int i=1; i<=c; i++)
{
long long temp=0;
for(int j=1; j<=c; j++)
{
long long h=i*j;
if(i*j<=c)
{
temp+=(long long)mu[j]*f[h];
}
else break;
}
ans+=temp*i;
}
cout<<2*ans-(long long)a*b<<endl;
}
}