[NOI2010 能量采集]

[关键字]:数学 数论 公约数

[题目大意]:太麻烦自己看题吧……

//================================================================================================================

[分析]:先说我自己的80分解法,枚举每个(x,y)然后因为这个点所在的直线就是:y=kx,k=(y/gcd(x,y))/(x/gcd(x,y))因为k要约分所以就是同时除以gcd(x,y)又因为所有点必须是整数,所以在这条线上的点数就等于x/(x/gce(x,y))=gcd(x,y)(因为从(0,0)开始,自己算一下)。复杂度为O(nm)80分,大概20分钟32行,对于本菜性价比很高。然后再说一下100分的做法,既然枚举x、y超时,那么可以反向思维枚举d=gcd(x,y),所有以d为公约数的(x,y)个数为f[d]=(n/d)*(m/d),那么所有以d为最大公约数的(x,y)的个数为Σf[d]-f[i*d](就是减去所有以d的倍数为公约数的个数),ans+=f[d]*(2*d+1)。

[代码]:

80分
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;

int N,M,ans;

int gcd(int x,int y)
{
if (x<y) swap(x,y);
while (y)
{
int t=x%y;
x=y;
y=t;
}
return x;
}

int main()
{
scanf("%d%d",&N,&M);
for (int y=1;y<=N;y++)
for (int x=1;x<=M;x++)
{
int temp=x/gcd(x,y);
ans+=2*((x-1)/temp)+1;
}
printf("%d\n",ans);
return 0;
}
100分
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;

int N,M;
long long ans,f[100010];

int main()
{
scanf("%d%d",&N,&M);
for (int i=min(N,M);i>=1;--i)
{
f[i]=(long long)(N/i)*(long long)(M/i);
for (int j=2;(j<=N/i && j<=M/i);++j)
f[i]-=f[i*j];
ans+=f[i]*(i*2-1);
}
printf("%lld\n",ans);
return 0;
}



你可能感兴趣的:([NOI2010 能量采集])