CQOI2014 数三角形
题目
给定正整数 \(N,M\) ,计算三点都在 \(N\times M\) 的网格的格点上的三角形有多少个
分析
可以发现,只要三点位于格点且不共线就能形成三角形,考虑计算三点共线的情况:
-
平行于 \(x\) 轴共线:
每行有 \(m+1\) 个点,共 \(n+1\) 行,所以有 \((n+1)\times\displaystyle\binom{m+1}{3}\) 种情况
-
平行于 \(y\) 轴共线:
每列有 \(n+1\) 个点,共 \(m+1\) 列,所以有 \((m+1)\times\displaystyle\binom{n+1}{3}\) 种情况
-
与 \(x,y\) 轴都不平行的共线:
设 \(AB\) 平行于 \(x\) 轴且 \(|AB|=i\) , \(AC\) 平行于 \(y\) 轴且 \(|AC|=j\) ,共线的三点中 \(B,C\) 为两个端点,那么 \(B,C\) 之间的格点数为 \(\gcd(i,j)+1\) ,所以共线的第三点有 \(\gcd(i,j)-1\) 种情况,易知形成 \(AB,AC\) 的有 \((n+1-i)\times(m+1-j)\) ,所以斜共线的总情况数为:
\[2\sum_{i=1}^n\sum_{j=1}^m (n+1-i)(m+1-j)[\gcd(i,j)-1] \]
综上,算出总和 \(\displaystyle\binom{(n+1)(m+1)}{3}\) 再减去上述三种反面情况即可
代码
#include<bits/stdc++.h>
using namespace std;
int gcd(int a, int b)
{
return a % b == 0 ? b : gcd(b, a % b);
}
int main()
{
int n, m;
long long ans;
cin >> n >> m;
n++;
m++;
ans = 1ll * (n * m) * (n * m - 1) * (n * m - 2) / 6;
ans -= 1ll * m * n * (n - 1) * (n - 2) / 6;
ans -= 1ll * n * m * (m - 1) * (m - 2) / 6;
for(int i = 1; i < n; i++)
for(int j = 1; j < m; j++)
ans -= 2ll * (n - i) * (m - j) * (gcd(i, j) - 1);
cout << ans << endl;
return 0;
}