有n个不同的点,问有多少组三元组能构成面积非0的三角形。
Input
单组测试数据。 第一行一个整数n (1 ≤ n ≤ 2000),表示点的数目。 接下来n行,每行包含两个整数 xi, yi ( -100 ≤ xi, yi ≤ 100),表示第i个点的坐标。输入保证点是两两不同的。
Output
输出合法的三角形数目。
Input示例
4 0 0 1 1 2 0 2 2
Output示例
3
求出每条直线上的点的个数,那么这条直线的点组成的三元组有i * (i-1) * (i-2)/6个, 再把n * (n-1) * (n-2)/6减去每条直线三元组的个数就是答案
至于每条直线上的点的个数怎么求:假设总共有n个点,有一条直线经过i, j, k三个点,那么把i遍历每个点(除了它自己)求出斜率,其中有两个斜率就是该直线的斜率(i-j, i-k)(也就是直线上点的个数是相同斜率的个数+1),所以判断出经过i点相同的斜率有多少个,那么经过i点的直线上的点的个数就可求出,是斜率个数+1;并且用p[斜率个数+1]++;
当i, j, k遍历完所有点后会发现,p[3] % 3 == 0
当每个点遍历完所有点后,p[i]/i表示具有i个点的直线的个数
#include <bits/stdc++.h>
#define maxn 2005
#define MOD 1000000007
using namespace std;
typedef long long ll;
struct Node{
Node(){
}
Node(int a, int b){
x = a;
y = b;
}
friend bool operator < (const Node&a, const Node&b){
return a.x < b.x || (a.x == b.x && a.y < b.y);
}
int x, y;
};
Node v[maxn];
int x[maxn], y[maxn];
int n, cnt;
int p[maxn];
int gcd(int a, int b){
return b ? gcd(b, a % b) : a;
}
int main(){
// freopen("in.txt", "r", stdin);
scanf("%d", &n);
getchar();
if(n < 3){
puts("0");
return 0;
}
for(int i = 1; i <= n; i++){
scanf("%d%d", x+i, y+i);
}
map<Node, int> ::iterator iter;
for(int i = 1; i <= n; i++){
cnt = 0;
for(int j = 1; j <= n; j++){
if(i == j)
continue;
if(x[i] == x[j]){
v[cnt++] = Node(-1, x[i]);
}
else if(y[i] == y[j]){
v[cnt++] = Node(-2, y[i]);
}
else{
int yy = y[i] - y[j];
int xx = x[i] - x[j];
int d = gcd(abs(xx), abs(yy));
xx /= d;
yy /= d;
if(xx < 0){
xx = -xx;
yy = -yy;
}
v[cnt++] = Node(xx, yy);
}
}
sort(v, v+cnt);
v[cnt].x = -4;
int k = 0;
for(int i = 1; i <= cnt; i++){
if(v[i].x != v[i-1].x || v[i].y != v[i-1].y){
p[i-k+1]++;
k = i;
}
}
}
ll ans = (ll)n * (n-1) * (n-2) / 6;
for(int i = 3; i <= n; i++){
if(p[i] == 0)
continue;
p[i] /= i;
ans -= (ll)i * (i - 1) * (i - 2) / 6 * p[i];
}
printf("%I64d\n", ans);
return 0;
}