链接
http://www.lydsy.com/JudgeOnline/problem.php?id=3505
题解
首先题目的坐标是从0开始取的...也就是说一共(N+1)(M+1)个点。
首先随便选三个点C[(N+1)(M+1),3],然后减去那些构成一条直线的。
有个定理是(i,j)到(0,0)的连线上的整数点的个数是gcd(i,j)(包含(0,0)而不包含(i,j))。
我们在坐标系内作线段,首先斜率为负数的通过翻转肯定能变成斜率为正数的,所以只需统计斜率为正数的(斜率为0或不存在的可以单独算)。
一条斜率>0的线段肯定能够通过平移使得左下端点和(0,0)重合,因此我们就到结论上来了,只需统计每个点和(0,0)连线上有多少点,然后乘上(N+1-i)*(M+1-j),即通过平移找到多少条这样的线段。总数减去这些不合法线段个数就是答案。
代码
//组合数学
#include <cstdio>
#include <algorithm>
#define ll long long
#define maxn 1100
using namespace std;
ll ans, N, M;
ll gcd(ll a, ll b){return !b?a:gcd(b,a%b);}
void work()
{
ll i, j, t=(N+1)*(M+1);
ans=t*(t-1)*(t-2)/6;
for(i=0;i<=N;i++)
{
for(j=0;j<=M;j++)
{
if(i==0 and j==0)continue;
t=(gcd(i,j)-1)*(N-i+1)*(M-j+1);
if(i==0 or j==0)ans-=t;
else ans-=t<<1;
}
}
}
int main()
{
scanf("%lld%lld",&N,&M);
work();
printf("%lld",ans);
return 0;
}