首先n++, m++;
转化为格点数,我们需要从 $n \times m$ 个格点中选出 $3$ 个合法格点构成三角形,那么显然我们只需要将 $C_{nm}^3$ 减去不合法的情况(即三点共线的情况)。
我们将三点共线的斜率分为三种情况分别统计:
- 斜率不存在(即竖直):$mC_n^3$
- 斜率为 $0$(即水平):$nC_m^3$
- 斜率存在且不为 $0$,斜率为正,与斜率为负是对称的,那么只考虑前者即可。
- 首先
n--, m--;
还原为长度。 - 我们在 $n \times m$ 的矩形中,枚举底为 $j$,高为 $i$ 的直角三角形,共有 $(n-i+1)(m-j+1)$ 种(当然,我们只考虑斜边斜率大于 $0$ 的情况)
- 这样的直角三角形,其斜边上的两个端点一定在格点上,我们只需要考察斜边上除了端点,还存在的格点的数量,结论:有 $\mathrm{gcd}(i,j)-1$ 个。那么这个三角形的斜边对三点共线的贡献即为:$(\mathrm{gcd}(i,j)-1)$ 种。
- 总结:共 $2(n-i+1)(m-j+1)(\mathrm{gcd}(i,j)-1)$ 种
- 首先
最后,利用容斥原理,从总数减去不合法的即可。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
LL n, m;
int gcd(int a, int b) {
return b ? gcd(b, a % b) : a;
}
LL C(LL a, LL b) {
return a * (a - 1) * (a - 2) / 6;
}
int main() {
cin >> n >> m;
n++, m++; // 转化为格点数
LL res = C(n * m, 3) - n * C(m, 3) - m * C(n, 3);
n--, m--; // 还原成长度
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++)
res -= 2 * (n - i + 1) * (m - j + 1) * (gcd(i, j) - 1);
}
cout << res << endl;
return 0;
}
为什么我直接枚举对角线是错的呢?
hh, 我刚开始枚举的是k=1的对角线是错的, 后来发现k有好多情况, 就懒得思考了, 直接看题解了hh.
STO
6
%%%%%%%%%
太妙了
我n和m没减减也可以ac是为什么
是Y总那种做法吧,要是的话,就是因为(m-j) 和(n-i)取到m和n时,res减法右侧全为0,所以多做了几次运算;对结果没有影响
妙啊!
写的真好,谢谢你%%%
1和2的方案数是不是写反了
是的 已修正 感谢!
枚举底为 i,高为 j 的直角三角形
应改为:枚举高为 i,底为 j 的直角三角形 吗?
谢谢提醒,确实写错了,已修正!
太强了,看了好多博客都没看懂为什么是gcd(i,j)-1,终于明白了,原来枚举的不是点,而是直角三角形
Orz
Orz