T2 数三角形
L u o g u Luogu Luogu 双倍经验:P3166
【问题描述】
Y Y Y手上有一个 n ∗ m n*m n∗m的网格,他想知道满足三个顶点都在格点上的三角形有多少个。
【输入格式】
输入一行,包含两个正整数 n n n和 m m m
【输出格式】
输出一行,包含一个正整数表示答案
【样例输入】
2 2
【样例输出】
76
【数据范围】
对于30%的数据,满足 n , m ≤ 10 n,m≤10 n,m≤10
对于100%的数据,满足 n , m ≤ 1000 n,m≤1000 n,m≤1000
【题解】
数学:组合
+
容斥
+
g
c
d
数学:组合+容斥+gcd
数学:组合+容斥+gcd
∑
三角形
=
∑
三个点任意组合
−
∑
三个点共线组合
\sum 三角形 = \sum三个点任意组合 - \sum三个点共线组合
∑三角形=∑三个点任意组合−∑三个点共线组合
∑ 三角形 = C ( m + 1 ) ( n + 1 ) 3 − ∑ 三点横向排列 − ∑ 三点纵向排列 − ∑ 三点斜向排列 \sum 三角形 = C_{(m + 1)(n + 1)}^{3} - \sum三点横向排列 - \sum三点纵向排列 - \sum三点斜向排列 ∑三角形=C(m+1)(n+1)3−∑三点横向排列−∑三点纵向排列−∑三点斜向排列
∑ 三角形 = C ( m + 1 ) ( n + 1 ) 3 − ( m + 1 ) C n + 1 3 − ( n + 1 ) C m + 1 3 − 2 ∗ ∑ 三点斜向排列,且斜率为正 \sum 三角形 = C_{(m + 1)(n + 1)}^{3} - (m + 1)C_{n+1}^3 - (n + 1)C_{m+1}^3 - 2*\sum三点斜向排列,且斜率为正 ∑三角形=C(m+1)(n+1)3−(m+1)Cn+13−(n+1)Cm+13−2∗∑三点斜向排列,且斜率为正
现在我们最大的问题就在于如何求出三个点斜向排列,且斜率为正的情况总数
给出一个特别的结论:
对于两个点坐标分别为
(
a
,
b
)
、
(
c
,
d
)
(a,b)、(c,d)
(a,b)、(c,d),另两点横纵坐标差为
i
=
∣
a
−
c
∣
,
j
=
∣
b
−
d
∣
i = |a - c|,j = |b - d|
i=∣a−c∣,j=∣b−d∣,由于斜率已经被我们限制死了,所以
i
=
a
−
c
,
j
=
b
−
d
i = a-c,j = b-d
i=a−c,j=b−d,这两个点连线上的格点共有
g
c
d
(
i
,
j
)
−
1
gcd(i,j)-1
gcd(i,j)−1 个,不难知道这样的点对共有
(
n
−
i
+
1
)
(
m
−
j
+
1
)
(n−i+1)(m−j+1)
(n−i+1)(m−j+1)个,三点斜向排列,且斜率为正的组合共有:
∑
i
=
1
n
∑
j
=
1
m
(
n
−
i
+
1
)
(
m
−
j
+
1
)
[
g
c
d
(
i
,
j
)
−
1
]
\sum_{i = 1}^n\sum_{j = 1}^m(n - i + 1)(m - j + 1)[gcd(i,j) - 1]
i=1∑nj=1∑m(n−i+1)(m−j+1)[gcd(i,j)−1]
上面的表达式注意要乘
2
2
2,这道题的最终结果即为:
C
(
m
+
1
)
(
n
+
1
)
3
−
(
m
+
1
)
C
n
+
1
3
−
(
n
+
1
)
C
m
+
1
3
−
2
∗
∑
i
=
1
n
∑
j
=
1
m
(
n
−
i
+
1
)
(
m
−
j
+
1
)
[
g
c
d
(
i
,
j
)
−
1
]
C_{(m + 1)(n + 1)}^{3} - (m + 1)C_{n+1}^3 - (n + 1)C_{m+1}^3 -2*\sum_{i = 1}^n\sum_{j = 1}^m(n - i + 1)(m - j + 1)[gcd(i,j) - 1]
C(m+1)(n+1)3−(m+1)Cn+13−(n+1)Cm+13−2∗i=1∑nj=1∑m(n−i+1)(m−j+1)[gcd(i,j)−1]
算呗。反正这个数学题我当时没推出来
w
w
w
www
www。这道题在洛谷上是个省选难度的,放在
T
2
T2
T2不是很能理解。
#include<bits/stdc++.h>
#define REG register
#define LL long long
using namespace std;
LL n,m,ans,nm;
int main(){
freopen("tri.in","r",stdin);
freopen("tri.out","w",stdout);
scanf("%lld %lld",&n,&m);
++n,++m; nm = n * m;
ans = nm * (nm - 1) * (nm - 2) / 6
- n * m * (m - 1) * (m - 2) / 6
- m * n * (n - 1) * (n - 2) / 6;
for(REG int i = 1;i < n;i++)
for(REG int j = 1; j < m ;j++)
ans -= (LL)2 * (LL)(__gcd(i , j) - 1)
* (LL)(n - i) * (LL)(m - j);
printf("%lld",ans);
return 0;
}
看完请留下你的痕迹 t h x thx thx