您的位置:首页 > 其它

【BZOJ】2005 [Noi2010]能量采集

2016-07-19 19:54 375 查看

[Noi2010]能量采集

题目链接

[Noi2010]能量采集

题目大意

中文题,题目意思很清楚,这里就不说了,总之就是要你求这个:

ans=∑x=1n∑y=1m[2(gcd(x,y)−1)+1]

题解

莫比乌斯反演 化简

可以化简:

ans=2∑x=1n∑y=1mgcd(x,y)−nm

可以看到现在的首要任务是求前面的和式,考虑到gcd(x,y)只有有限个值,我们设

f(d):gcd(x,y)=d的(x,y)的对数

当然x和y都在范围内。

这样的话我们的ans可以改写为:

ans=2∑dd⋅f(d)−nm

看到首要任务是求f(d),这里我们用莫比乌斯反演,于是我们又设:

F(d):d|gcd(x,y)的(x,y)的对数

又因为:

F(d)=nd⋅md

根据反演公式,有:

f(x)=∑x|dμ(dx)⋅nd⋅md

到这里基本又是一些老东西了,令T=xd什么的,最后可以化简到:

f(x)=∑TnTmT∑x|Tμ(Tx)

把x换成d,再带入ans,得到:

ans=∑TnTmT∑d|Td⋅μ(Tx)

到这里,我们的μ(x)函数有一个性质:

∑d|nd⋅μ(nd)=ϕ(n)

这个可以用用欧拉函数的性质∑d|nϕ(d)=n这个式子莫比乌斯反演得来,所以我们可以把ans变为

ans=∑TnTmT⋅ϕ(T)

可以看到最后的式子非常简单,先筛出phi(d)然后分块求和即可。

代码

不能用%I64d,会WA

#include <iostream>
#include <cstring>
#include <cstdio>
#define LL long long
#define maxn 100005

using namespace std;

LL n,m,p[maxn-5],cnt,phi[maxn];
bool vis[maxn-5];

void setup(int high)
{
cnt=0;
memset(p,0,sizeof(p));
memset(vis,0,sizeof(vis));
memset(phi,0,sizeof(phi));
phi[1]=1;
for (int i=2;i<=high;i++)
{
if (!vis[i])
{
vis[i]=1; p[cnt++]=i;
phi[i]=i-1;
}
for (int j=0;j<cnt && i*p[j]<=high;j++)
{
vis[i*p[j]]=1;
if (i%p[j]) phi[i*p[j]]=phi[i]*(p[j]-1);
else
{
phi[i*p[j]]=phi[i]*p[j];
break;
}
}
}
for (int i=1;i<=high;i++) phi[i]+=phi[i-1];
}

int main()
{
scanf("%lld%lld",&n,&m);
setup(min(n,m));
LL last=0,t=min(n,m);
LL ans=0;
for (int i=1;i<=t;i=last+1)
{
last=min(n/(n/i),m/(m/i));
ans+=(LL) (n/i)*(m/i)*(phi[last]-phi[i-1]);
}
ans=(LL)ans*2-n*m;
printf("%lld\n",ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: