链接:戳这里
How Many Triangles
Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
Alice has n points in two-dimensional plane. She wants to know how many different acute triangles they can form. Two triangles are considered different if they differ in at least one point.
Input
The input contains multiple test cases.
For each test case, begin with an integer n,
next n lines each contains two integers xi and yi.
3≤n≤2000
0≤xi,yi≤1e9
Any two points will not coincide.
Output
For each test case output a line contains an integer.
Sample Input
3
1 1
2 2
2 3
3
1 1
2 3
3 2
4
1 1
3 1
4 1
2 3
Sample Output
0
1
2
题意:
平面上n个点,问能组成多少个锐角三角形
思路:
三角形总共有C(n,3)个,对应的 锐角+钝角+直角=总和为3*C(n,3)
假设有x个钝角,y个直角,钝角和直角分别对应两个锐角
那么锐角三角形的数量为:(3*C(n,3)-2*x-x-2*y-y)/3 = C(n,3)-x-y
现在统计有多少个钝角和直角!
枚举三角形的一条边,找出这条边左手边有多少个点能与该组成锐角三角形 O(n*n*logn)
嗯!我现在来扣扣细节。
一条边通过两个点确定,不妨设点a和点b去找符合条件的点c个数。
n-1个点分别与点a构成n-1条向量v[i],极角排序一下。就可以更加方便的找出一条向量v左手边有多少个点满足条件
首先枚举一条向量v,那么能组成锐角三角形的点(假设这个点与点a构成向量w)肯定在v的左手边且夹角<90度
这里一个技巧cross(v,w)=0为两向量共线,dot(v,w)>0为两向量同向
cross(v,w)>0叉积为正则表示w在v的左手边,dot(v,w)向量为正数表示夹角<90度 为0表示90度 为负数表示>90度
然后每次统计有多少向量w与当前向量v夹角>=90,减去就可以了
当然了,最后还需要减去三点共线的,三点共线也就是两向量共线,假设数目为tmp,枚举的时候会重复一次。
减去的时候tmp/2就可以了。 参考了Q神的代码,怒拿一血%%%
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<vector>
#include <ctime>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<iomanip>
#include<cmath>
#include<bitset>
#define mst(ss,b) memset((ss),(b),sizeof(ss))
///#pragma comment(linker, "/STACK:102400000,102400000")
typedef long long ll;
typedef long double ld;
#define INF (1ll<<60)-1
#define Max 1e9
using namespace std;
struct point{
ll x,y;
point(ll x=0,ll y=0):x(x),y(y){}
point operator - (const point &t)const{
return point(x-t.x,y-t.y);
}
ll operator * (const point &t)const{ /// 叉积 : 有向面积
return x*t.y-y*t.x;
}
ll operator ^ (const point &t)const{ /// 点积 : 有向长度
return x*t.x+y*t.y;
}
bool operator < (const point &t)const{ /// 极角排序
bool up[2]={0,0};
if(y>0 || (y==0 && x>0)) up[0]=1;
if(t.y>0 || (t.y==0 && t.x>0)) up[1]=1;
if(up[0]^up[1]) return up[0];
return (*this)*t ? (*this)*t>0 : ((*this)^(*this))<(t^t);
}
}p[2020],v[4020];
/*
三角形总共有C(n,3)个,对应的锐角钝角直角总和为3*C(n,3)
假设有x个钝角,y个直角 钝角和直角分别对应两个锐角
那么锐角三角形的数量为:(3*C(n,3)-2*x-x-2*y-y)/3 = C(n,3)-x-y
统计有多少个钝角和直角!
*/
int main(){
int n;
while(scanf("%d",&n)!=EOF){
for(int i=0;i<n;i++) scanf("%I64d%I64d",&p[i].x,&p[i].y);
ll ans=1LL*n*(n-1)*(n-2)/6,tmp=0;
for(int i=0;i<n;i++){
int tot=0;
for(int j=0;j<n;j++){
if(i==j) continue;
v[tot++]=p[j]-p[i]; /// 向量i->j
}
sort(v,v+tot);
for(int j=0;j<tot;j++) v[j+tot]=v[j];
ll num=0;
for(int j=1;j<tot;j++){
if(v[j-1]*v[j]==0 && (v[j-1]^v[j])>0) num++;
///两条向量共线且同向->三点共线
else num=0;
tmp+=num;
}
int p1=0,p2=0;
for(int j=0;j<tot;j++){
while(p1<=j || (p1<j+tot && v[p1]*v[j]<0 && (v[p1]^v[j])>0)) p1++;/// v[j]左边的锐角个数
while(p2<=j || (p2<j+tot && v[p2]*v[j]<0))p2++;/// v[j]左边的锐角+钝角+直角个数
ans-=p2-p1;
}
}
printf("%I64d\n",ans-tmp/2);
}
return 0;
}
/*
3
1 1
2 2
2 3
3
1 1
2 3
3 2
4
1 1
3 1
4 1
2 3
*/