题目链接:

http://www.lydsy.com/JudgeOnline/problem.php?id=3505


题目:

Description

给定一个nxm的网格,请计算三点都在格点上的三角形共有多少个。下图为4x4的网格上的一个三角形。

注意三角形的三点不能共线。

Input

输入一行,包含两个空格分隔的正整数m和n。

Output

输出一个正整数,为所求三角形数量。

Sample Input

2 2

Sample Output

76

数据范围

1<=m,n<=1000


思路:

  先计算出所有点中任意选三个点的方案数,再减去三点共线的三角形数就可以。

  可以注意到,当选定$(0, 0)$为起点时,和终点$(i, j)$所形成的向量上就会有$gcd(i,j)$个整数点。设点$(x, y)$为点$P$,当某个向量的终点变为点$P$时,就可以和之前的$gcd(x,y) - 1$个点以及零点形成$gcd(x,y)-1$个三点共线的三角形。此时,线段$(0,0)\to (x,y)$的贡献就为$gcd(x,y)-1$,由于其还可以向右和向下平移,故这条向量的贡献还要再乘以$(n-x) \times (m-y)$,此外,以$(0,0)$为起点的向量的斜率的范围为$[\frac {\pi}{2}, -\frac {\pi}{2}]$,为了补全左半边,还要乘以$2$,所以一条不垂直且不水平的向量对答案产生的负贡献为$(gcd(x,y)-1)\times (n-x) \times (m-y) \times 2$。


实现:

#include <bits/stdc++.h>
typedef long long ll;
ll C(ll tmp) { return tmp * (tmp - 1) * (tmp - 2) / 6; }
ll gcd(ll p, ll q) { return q ? gcd (q, p % q) : p; }
ll n, m;
int main() {
    scanf("%lld%lld", &n, &m); n++, m++;
    ll ans = C(n * m);
    for (int i = 0; i < n; i++) for (int j = 0; j < m; j++) {
            if (i == 0 && j == 0) continue ;
            ans -= (gcd(i, j) - 1) * (n - i) * (m - j) * (1 + (i != 0 && j != 0));
        }
    printf("%lld\n", ans);
    return 0;
}
最后修改:2018 年 03 月 20 日
谢谢老板!