题意:http://www.lydsy.com/JudgeOnline/problem.php?id=2005
实际上把这些被挡住的点的坐标和能量值列举出来可以发现有个公式:
“对于坐标系第一象限任意的整点(即横纵坐标均为整数的点)p(n,m),
其与原点o(0,0)的连线上除过原点整点的个数为gcd(n,m)。
其他象限上个数则为gcd(abs(n),abs(m))”
答案就是sum(2*gcd(x,y)-1) [x=(1..n),y=(1..m)]
//证明:http://www.cnblogs.com/huhuuu/archive/2011/11/25/2263803.html
公式进一步化简:
对于本题,gcd(x,y)的值一定不会超过max(n,m)。这样直接枚举gcd(x,y)所有可能的值,再套用hdu 1695给出来的那个G函数就行啦~
#include <iostream>
#include <cstring>
#include <cmath>
using namespace std;
#define LL long long
#define MMX 100010
int mu[MMX],msum[MMX];
LL n,m;
bool check[MMX];
int prime[MMX]; void Moblus()
{
memset(check,false,sizeof(check));
mu[] = ;
int tot = ;
for(int i = ; i <= MMX; i++)
{
if( !check[i] )
{
prime[tot++] = i;
mu[i] = -;
}
for(int j = ; j < tot; j++)
{
if(i * prime[j] > MMX) break;
check[i * prime[j]] = true;
if( i % prime[j] == )
{
mu[i * prime[j]] = ;
break;
}
else
{
mu[i * prime[j]] = -mu[i];
}
}
}
msum[]=mu[];
for (int i=;i<=MMX;i++)
msum[i]=msum[i-]+mu[i];
} LL G(int n,int m) //加分块优化
{
LL ans = ;
if(n > m) swap(n,m);
for(int i = , la = ; i <= n; i = la+)
{
la = min(n/(n/i),m/(m/i));
ans += (LL)(msum[la] - msum[i-])*(n/i)*(m/i); //事先预处理:msum[n]=SUM(mu[1..n])
}
return ans;
} int main()
{
Moblus();
cin>>n>>m;
LL sum=;
for (LL i=;i<=max(m,n);i++)
sum+=i*G(m/i,n/i);
sum=*sum-n*m;
cout<<sum<<endl;
}