Solution
首先看一种(错误/麻烦)的做法;
矩阵长为 m m m 高为 n n n, 即有 T = ( m + 1 ) ∗ ( n + 1 ) T = (m+1)*(n+1) T=(m+1)∗(n+1)个点, 我们选三个点 T 3 T^3 T3, 此时不合法的三角形有几种情况: (一条线, 有两个点重合, 三个点重合), 而且抛去这些情况后, 假如得到 t t t, 那么你需要将 t / ( 3 ! ) t / (3!) t/(3!)后, 才是答案; (因为一个特定的合法三角形, 将三个点线性化后, 他有 3 ! 3! 3!种情况)
这很麻烦, 没必要将总方案设置为
T
3
T^3
T3, 一个三角形的三个点 不需要将他线性化 (也就是比如所有位置ID为1,2,3,4,...,T
, 我们选择了三个位置a, b, c
; 如果总方案是
T
3
T^3
T3, 则a,b,c
会对应有
3
!
3!
3!个方案, 即abc, acb, bac, ...
; 这是没有必要的, 他就去对应一个方案即可);
.
最好的处理是: 我们直接选
3
3
3个坑即可, 即将总方案数设置为
C
T
3
C_T^3
CT3 表示选择三个不同的点方案, 毕竟一个合法三角形的三个点 肯定是不同的;
@Delimiter
总方案
C
T
3
C_T^3
CT3, 此时不合法的情况 只有(三个点在同一直线上)
1 (斜率垂直)
m
∗
C
n
3
m * C_n^3
m∗Cn3;
2 (斜率水平)
n
∗
C
m
3
n * C_m^3
n∗Cm3;
3 (斜率不为0)
重点考虑第三种情况;
性质: 长高为
a
∗
b
a*b
a∗b的直角三角形 (注意长度为
a
a
a是经过了
a
+
1
a+1
a+1的点), 有
g
c
d
(
a
,
b
)
+
1
gcd(a,b) + 1
gcd(a,b)+1个方格点 坐落在他的斜边上
证明: 令
g
=
g
c
d
(
a
,
b
)
,
A
=
a
/
g
,
B
=
b
/
g
g = gcd(a,b), A = a/g, B = b/g
g=gcd(a,b),A=a/g,B=b/g, 将长和高都分为了
g
g
g个长度为
A
,
B
A, B
A,B的子段; 于是, 可以发现 斜边也可以分为
g
g
g的子段 每个子段处于一个
A
∗
B
A*B
A∗B的矩形里;
@Delimiter
看一种错误的做法;
对于整个方格矩形边框
d-----c
| |
a-----b
只考虑斜率
>
0
> 0
>0的直线 (斜率
<
0
<0
<0的方案 与其相等), 则该直线有两种情况: (1 相交于
a
b
,
b
c
ab, bc
ab,bc) 或者 (2 相交于
a
d
,
d
c
ad, dc
ad,dc); 而且, 该直线与该矩阵边框的交点 肯定位于方格点上;
于是, 将该矩形边框分为两个三角形
a
b
c
,
a
c
d
abc, acd
abc,acd; 单独考虑
a
b
c
abc
abc三角形里面的;
其实这是错误的, 错在: 一个斜率 > 0 >0 >0的直线, 他可能相交于 a d , b c ad, bc ad,bc; 因此不可以将整个矩形边框分为两半来考虑;
而且 更重要的是: 该直线与该矩形边框的交点, 不一定位于方格点上;
意识到这两个错误非常重要;
@Delimiter
再看一种错误思路;
对于一个斜率 > 0 > 0 >0的直线, 假如该矩形里有 k ≥ 3 k \geq 3 k≥3个点 是位于该直线上的; 令 l , r l,r l,r为两个在该直线上的方格点 且距离最远; 那么由上面的性质, 得由 l , r l,r l,r所确立的三角形 ( l l l向右延伸直线 r r r向下延伸直线, 所构成的三角形), 他的长高 a , b a,b a,b 满足 G C D ( a , b ) = k − 1 GCD(a, b) = k - 1 GCD(a,b)=k−1;
但具体 a , b a,b a,b的取值 是有很多种的;
所以, 通过枚举斜边上的点数 k k k; 首先, 他所对应的斜率有很多, 而且即便斜率确定了, 他所对应的三角形长高 a , b a,b a,b 也有很多取值;
@Delimiter
还是基于上面思路: 对于一个斜率 > 0 > 0 >0的直线, 假如该矩形里有 k ≥ 3 k \geq 3 k≥3个点 是位于该直线上的; 令 l , r l,r l,r为两个在该直线上的方格点 且距离最远; 那么由上面的性质, 得由 l , r l,r l,r所确立的三角形 ( l l l向右延伸直线 r r r向下延伸直线, 所构成的三角形), 他的长高 a , b a,b a,b 满足 G C D ( a , b ) = k − 1 GCD(a, b) = k - 1 GCD(a,b)=k−1;
此时, 重点放在
a
,
b
a,b
a,b上, 不去考虑这个直线的斜率 因为一旦
a
,
b
a,b
a,b确立了 直线斜率也就确立了;
而且, 这个
k
k
k也不是通过枚举了, 因为
a
,
b
a,b
a,b确立了,
k
k
k也就确立了;
于是, 我们枚举长高为
a
∗
b
a*b
a∗b的直角三角形 令
k
=
G
C
D
(
a
,
b
)
+
1
≥
2
k = GCD(a,b) + 1 \geq 2
k=GCD(a,b)+1≥2 (即斜边上
k
≥
3
k \geq 3
k≥3个点); 这样的三角形, 在整个矩形里, 有
(
m
−
a
+
1
)
∗
(
n
−
b
+
1
)
(m - a + 1) * (n - b + 1)
(m−a+1)∗(n−b+1)个 这样的三角形 (形象的说,
a
∗
b
a*b
a∗b的直角三角形 上下左右移动 会有
(
m
−
a
+
1
)
∗
(
n
−
b
+
1
)
(m-a+1)*(n-b+1)
(m−a+1)∗(n−b+1)个不同的位置); 于是, 每个斜边 有
C
k
3
C_k^3
Ck3种可能的选择;
.
这又错了… 这样是会有重叠方案的, 比如:
k
=
4
k = 4
k=4, 我们在某个位置
x
,
y
x,y
x,y时, 那么(斜边上最上面的
3
3
3个点) 我们选择了这个方案, 而当我们右移一位再上移一位后 到达
x
+
1
,
y
+
1
x+1,y+1
x+1,y+1的位置 那么(斜边上最下面的
3
3
3个点) 我们也会选择这个方案, 因此重复了;
此时, 你可以固定的 强制的必须选择 (斜边的两个端点), 这样 就不会重复了, 即一个三角形 他的斜边上 不是有 C k 3 C_k^3 Ck3种 而是有 ( k − 2 ) (k-2) (k−2)种; 这就对了;
可以理解为: 对于一种不合法的选择:
a
,
b
,
c
a,b,c
a,b,c三个点, 其他们形成一个斜率
>
0
>0
>0的直线; 令
l
,
r
l,r
l,r为最左侧/最右侧的两个端点;
则此时,
l
,
r
l,r
l,r形成了一个长高
A
,
B
A,B
A,B的三角形, 且该三角形的斜边的端点 就是
l
,
r
l,r
l,r;
Code
long long ans = 0;
ans += CompPerm_C( M * N, 3);
ans -= M * CompPerm_C( N, 3);
ans -= N * CompPerm_C( M, 3);
for( int i = 2; i <= m; ++i){
for( int j = 2; j <= n; ++j){
int g = GCD( i, j);
if( g < 2){ continue;}
ans -= (m - i + 1) * (n - j + 1) * 2 * (g - 1);
}
}
cout<<ans;