bzoj 2005

版权声明:本博客中有原创标签的内容均为博主原创,转载请注明出处! https://blog.csdn.net/lleozhang/article/details/83416791

非常好的题

简化一下题意,我们可以发现:(0,0)与(x,y)之间经过的整点的数量等于gcd(x,y)-1!

利用这条性质,我们可以列出一个表达式:\large ans=\sum_{i=1}^{n}\sum_{j=1}^{m}(2*gcd(i,j)-1)

稍微化简一下,得:

\large ans=2\sum_{i=1}^{n}\sum_{j=1}^{m}gcd(i,j)-nm

接下来,是最重要的一部分:

引理:

\large x=\sum_{d|X}\psi (d)

那么,将x=gcd(i,j)代入,得:

\large ans=2\sum_{i=1}^{n}\sum_{j=1}^{m}\sum_{d|i,d|j}\psi(d)-nm

再化简一下,得:

\large ans=2\sum_{d=1}^{min(n,m)}\psi(d)*\left \lfloor \tfrac{n}{d} \right \rfloor*\left \lfloor \tfrac{m}{d} \right \rfloor-nm

由于n,m很小,所以预处理出φ,然后暴力枚举计算即可

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#define ll long long
#define maxn 1000000
using namespace std;
int phi[1000005];
int pri[1000005];
bool used[1000005];
int n,m;
int tot=0;
void init()
{
	phi[1]=1;
	for(int i=2;i<=maxn;i++)
	{
		if(!used[i])
		{
			pri[++tot]=i;
			phi[i]=i-1;
		}
		for(int j=1;j<=tot&&i*pri[j]<=maxn;j++)
		{
			used[i*pri[j]]=1;
			if(i%pri[j]==0)
			{
				phi[i*pri[j]]=phi[i]*pri[j];
				break;
			}else
			{
				phi[i*pri[j]]=phi[i]*(pri[j]-1);
			}
		}
	}
}
int main()
{
	init();
	scanf("%d%d",&n,&m);
	ll ans=0;
	for(int i=1;i<=min(n,m);i++)
	{
		ans+=(ll)phi[i]*(ll)(n/i)*(ll)(m/i);
	}
	ans*=2ll;
	ans-=(ll)n*(ll)m;
	printf("%lld\n",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/lleozhang/article/details/83416791
今日推荐