计数题,听妙的。
首先n ++,m ++;因为是格点数,题目给的是边长。
考虑容斥,所有可能组成三角形的个数就是任意取三个点即C(nm,3)减去不合法的就是在同一条直线上的。
同一条直线分为横竖斜。横竖直接考虑C(n,3)*m和C(m,3)*n
斜边稍微复杂一点,画个图。
我们考虑所有不合法的直线,两点确定一条直线,所以枚举两个点,复杂度O(n^2m^2)死掉了,把其中一个点等价转换到坐标系(0,0)点处,枚举另一个点。
显然在这两条直线间经过的格点数目为gcd(i,j)-1。考虑如果gcd(i,j)==1,那么两点中间就是直接到达的。用反证法,如果gcd(i,j)!=1且中间没有点,那么(i/gcvd(i,j),j/gcd(i,j))这个点必定会挡住线段的两个端点,所以不成立。即两点之间格点数目为(gcd(i,j)-1)。当前i,j考虑的点由于两个端点固定,可以把他进行等价平移,平移个数就是(n-i)*(m-j),最后因为矩阵对称性,现在的直线相当于斜率为负,考虑斜率是正的时候*2,就好了。
代码简单
#include <bits/stdc++.h> using namespace std; #define int long long const int N = 1e3 + 10; int n, m; int a[N][N], cnt; int q[N<<2][2]; int gcd(int a,int b){ if(b == 0) return a; return gcd(b, a % b); } int sss; signed main(){ cin >> n >> m; n ++;m ++; int ans = n * m; ans = ans * (ans - 1) * (ans - 2) / 6; ans = ans - (m * n * (n - 1) * (n - 2) / 6); ans = ans - (n * m * (m - 1) * (m - 2) / 6); for(int i = 1;i < n;++ i){ for(int j = 1;j < m;++ j){ ans -= 2 * (gcd(i,j)-1) * (n - i) * (m - j); } } cout << ans; return 0; }