51nod-1451 合法三角形

原题链接

题目来源:  CodeForces
基准时间限制:1 秒 空间限制:131072 KB 分值: 80  难度:5级算法题
 收藏
 关注

有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;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值