您的位置:首页 > 其它

BZOJ 2005: [Noi2010]能量采集(莫比乌斯反演)

2017-07-30 21:30 375 查看
http://www.lydsy.com/JudgeOnline/problem.php?id=2005

题意:



思路:
首先要知道一点是,某个坐标(x,y)与(0,0)之间的整数点的个数为gcd(x,y),这样一来每个坐标损失的能量为2*gcd(x,y)-1。
所以在这道题目中要计算的就是


f(d)表示gcd(x,y)=d的对数,那么F(d)表示d|gcd(x,y)的对数。
根据反演可以得到,


那么这道题的答案就是,


#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<sstream>
#include<vector>
#include<stack>
#include<queue>
#include<cmath>
#include<map>
#include<set>
using namespace std;
typedef long long ll;
typedef pair<int,int> pll;
const int INF = 0x3f3f3f3f;
const int maxn = 100000 + 5;

bool check[maxn];
int prime[maxn];
int mu[maxn];
ll sum[maxn];

void Mobius()
{
memset(check, false, sizeof(check));
mu[1] = 1;
int tot = 0;
for (int i = 2; i <= maxn; i++)
{
if (!check[i])
{
prime[tot++] = i;
mu[i] = -1;
}
for (int 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];
}
}
}
sum[0]=0;
for(int i=1;i<maxn;i++)
sum[i]=sum[i-1]+mu[i];
return ;
}

ll solve(int n, int m)
{
if(n>m)  swap(n,m);
ll tmp=0;
for(int i=1,last=0;i<=n;i=last+1)
{
last=min(n/(n/i),m/(m/i));
tmp+=(sum[last]-sum[i-1])*(n/i)*(m/i);
}
return tmp;
}

int n, m;

int main()
{
//freopen("in.txt","r",stdin);
Mobius();
while(~scanf("%d%d",&m,&n))
{
ll ans=0;
for(int i=1;i<=min(n,m);i++)   //枚举d
ans+=solve(n/i,m/i)*i;     //这儿求gcd(x,y)=d的对数,但是如果/i的话就相当于计算gcd(x,y)=1的对数
//简化了计算
printf("%lld\n",2*ans-(ll)n*m);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: