BZOJ2005 莫比乌斯反演

题意: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

公式进一步化简:

BZOJ2005       莫比乌斯反演

对于本题,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;
}
上一篇:UART


下一篇:JDK源码分析(4)之 LinkedList 相关