三角形(triangle) \operatorname{三角形(triangle)} 三角形(triangle)
题目链接: SSL比赛 1143 \operatorname{SSL比赛\ 1143} SSL比赛 1143
题目
平面上有 n n n 个点,求出用这些点可以构成的三角形数。
输入
第一行一个整数 n n n 。
接下来 n n n 行,每行两个整数,表示点的坐标。
输出
输出仅一个整数,表示所求答案。
样例输入
5
0 0
1 1
1 -1
-1 -1
-1 1
样例输出
8
数据范围
对于 50 % 50\% 50% 的数据, n < = 300 n<=300 n<=300 。
对于 100 % 100\% 100% 的数据, n < = 3000 n<=3000 n<=3000 ,坐标的绝对值不超过 1 0 4 10^4 104 ,保证没有重合的点。
思路
这道题是一道数学题。
我们可以知道,三个点不能组合成三角形,当且仅当这三个点在一条直线上,就是其中一个点和另外两个点分别组成的直线的斜率是相等的。
啥?你不知道怎么求斜率?!
行吧。
就是我们可以知道直线的函数可以表示为 y = k x + b y=kx+b y=kx+b ,而斜率就是 k k k ,可以表示出这条直线有多斜,是怎样斜的。
那我们知道直线上的两个点怎么求斜率呢?
我们设两点坐标分别为 ( x 1 , y 1 ) (x_1,y_1) (x1,y1) 和 ( x 2 , y 2 ) (x_2,y_2) (x2,y2) 。
那带进去前面的式子,就是这个:
{ y 1 = k x 1 + b y 2 = k x 2 + b \begin{cases}y_1=kx_1+b\\y_2=kx_2+b\end{cases} {
y1=kx1+by2=kx2+b
然后我们就解二元一次方程,就可以得到:
{ k = y 1 − y 2 x 1 − x 2 b = y 1 − x 1 × y 1 − y 2 x 1 − x 2 \begin{cases}k=\dfrac{y_1-y_2}{x_1-x_2}\\b=y_1-x_1\times\dfrac{y_1-y_2}{x_1-x_2}\end{cases} ⎩⎪⎨⎪⎧k=x1−x2y1−y2b=y1−x1×x1−x2y1−y2
那我们就得到了 k k k 。
我们可以先算出所有点中选三个点的排列有多少,然后再减去不能组合成的。
前面的排列很好求,如果点的数量是 n n n ,那就是 n × ( n − 1 ) × ( n − 2 ) / 2 / 3 n\times (n-1)\times(n - 2) / 2 / 3 n×(n−1)×(n−2)/2/3 。
但是不能组合成的种数怎么求呢?
我们可以枚举一个点,然后求出它和所有点组成的直线的斜率。然后排序,就可以看出斜率相同的种数和每一种有多少个的。
那我们可以知道,对于每一种斜率,设有 x x x 个这样的斜率,那答案就要减去 x × ( x − 1 ) / 2 x\times (x-1) / 2 x×(x−1)/2 。
但是有一个特殊的东西,就是斜率不存在。
为什么会有这种情况呢,因为我们可以看到,如果这条直线是一条平行于 y y y 轴的直线,那 x 1 x_1 x1 就会等于 x 2 x_2 x2 ,就会使 x 1 − x 2 = 0 x_1-x_2=0 x1−x2=0 ,造成除数是 0 0 0 的情况。
那我们就要特殊判断一下,单独拎出来处理即可。
代码
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
ll n, x[3001], y[3001], ans;
ll shu, num, nownum;
double xie[3001];
int main() {
scanf("%lld", &n);//读入
for (ll i = 1; i <= n; i++)
scanf("%lld %lld", &x[i], &y[i]);
ans = n * (n - 1) / 2 * (n - 2) / 3;//全部排列
for (ll i = 1; i < n; i++) {
shu = 0;
num = 0;
for (ll j = i + 1; j <= n; j++)
if (x[i] == x[j]) shu++;//图像是竖着的(特殊处理)
else xie[++num] = (double)(y[i] - y[j]) / (double)(x[i] - x[j]);//斜率
sort(xie + 1, xie + num + 1);//排序
ans -= shu * (shu - 1) / 2;//特殊处理竖着的这条这线上的点
nownum = 1;
for (ll j = 2; j <= num; j++)//找到相同的斜率的数量
if (xie[j] == xie[j - 1]) nownum++;
else {
ans -= nownum * (nownum - 1) / 2;//处理这个斜率上的点
nownum = 1;
}
ans -= nownum * (nownum - 1) / 2;
}
printf("%lld", ans);//输出
return 0;
}