题目
给定一个nxm的网格,请计算三点都在格点上的三角形共有多少个。下图为4x4的网格上的一个三角形。注意三角形的三点不能共线。
题解
- 首先所有的方案数为 C ( n + 1 ) × ( m + 1 ) 3 C_{(n+1)\times(m+1)}^3 C(n+1)×(m+1)3
- 那么我们考虑什么时候三个点构不成三角形——三点共线
- 当三点分别平行于 x x x轴, y y y轴共线时方案数为 ( n + 1 ) × C m + 1 3 (n+1)\times C_{m+1}^{3} (n+1)×Cm+13和 ( m + 1 ) × C n + 1 3 (m+1)\times C_{n+1}^{3} (m+1)×Cn+13
- 我们考虑斜着三点共线的情况
- 对于两点 ( x 1 , y 1 ) , ( x 2 , y 2 ) ( x 2 > x 1 , y 2 > y 1 ) (x_1,y_1),(x_2,y_2)(x_2>x_1,y_2>y_1) (x1,y1),(x2,y2)(x2>x1,y2>y1)他们之间的整点个数为 g c d ( x 2 − x 1 , y 2 − y 1 ) − 1 gcd(x_2-x_1,y_2-y_1)-1 gcd(x2−x1,y2−y1)−1
- 那么我们肯定不能枚举 x 1 , y 1 , x 2 , y 2 x_1,y_1,x_2,y_2 x1,y1,x2,y2这样复杂度是 O ( n 4 ) O(n^4) O(n4),我们考虑将一个点一到原点,因为所有长度相同的线段,所贡献的答案是一样的,我们只需考虑有几条长度相同的线段即可。 那我们考虑长度相同的线段有几条。假设另一端点为 ( i , j ) (i,j) (i,j)(一个端点为 ( 0 , 0 ) (0,0) (0,0)),那么条数为 ( n − i + 1 ) × ( m − j + 1 ) (n-i+1)\times (m-j+1) (n−i+1)×(m−j+1)(画个图就很好理解了),那么不合法的状态就为 ( g c d ( i , j ) − 1 ) × ( n − i + 1 ) × ( m − j + 1 ) (gcd(i,j)-1) \times (n-i+1)\times (m-j+1) (gcd(i,j)−1)×(n−i+1)×(m−j+1)
- 用所有方案数减去不合法的方案数即可(这种思想似乎叫做补集转化?)
code
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e3 + 100;
int m, n;
inline int gcd(int x, int y) {
return y ? gcd(y, x % y) : x;
}
int main() {
scanf("%d%d", &m, &n); m++, n++;
int all = m * n;
LL ans = 1ll * all * (all - 1) * (all - 2) / 6;
if (m >= 3) ans -= 1ll * n * m * (m - 1) * (m - 2) / 6;
if (n >= 3) ans -= 1ll * m * n * (n - 1) * (n - 2) / 6;
// cout << ans << endl;
for (int i = 1; i < n; ++i) {
for (int j = 1; j < m; ++j) {
ans -= 2 * 1ll * (LL)(gcd(i, j) - 1) * (LL)(n - i) * (LL)(m - j);
}
}
printf("%lld\n", ans);
return 0;
}