[solution] JZOJ3493 三角形
Description
平面上有n个点,求出用这些点可以构成的三角形数。
Input
第一行一个整数n。
接下来n行,每行两个整数,表示点的坐标。
Output
输出仅一个整数,表示所求答案。
Sample Input
5
0 0
1 1
1 -1
-1 -1
-1 1
Sample Output
8
Data Constraint
对于50%的数据,n<=300。
对于100%的数据,n<=3000,坐标的绝对值不超过10^4,保证没有重合的点。
华丽的分割线
这个题就是求$n$个点之间能构成的三角形数
50分解法
暴力的解法就是$O(n^3)$的复杂度判三点共线,判定的方法就是看斜率是否相等,这里可以使用$x_1y_2=x_2y_1$来判断(自己去证),这样子可以搞定斜率为不存在(连线垂直于$x$轴)的情况。
100分做法
考虑到这里的$n$是$\leq3000$的,所以直接去暴力判重会爆掉,所以说我们要对其进行优化,正解给出了一个玄学的思路:
通过排序来完成判重
是不是很意外??
没错就是这种古老的被我们遗忘的做法
具体做法:
每次枚举一个点$i$,枚举编号大于等于$i$的顶点,计算出斜率$k$,显然,如果三点共线的话,斜率是相同的,如果相同的有$p$个,那么根据排列组合就应该在总数中减去$C^p_3$个,注意判重的精度要高一些,不然会炸。原来的总数是多少呢,显然是$C^n_3$。
AC Code
841ms,364KB
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
#define ll long long
#define maxn 3005
using namespace std;
ll n,a[maxn],b[maxn];
double k[maxn];
void init(){
scanf("%lld",&n);
for(ll i=1;i<=n;i++)
scanf("%lld %lld",a+i,b+i);
return;
}
ll cmp(double a,double b){return a-b<0.00000001;}
void solve(){
ll ans=n*(n-1)*(n-2)/6;
for(ll i=1;i<=n-2;i++){
for(ll j=1;j<=n;j++) k[i]=99999999999.9999;
for(ll j=i+1;j<=n;j++)
if(a[i]==a[j]) k[j]=999999999999.9999;
else k[j]=(double)(b[i]-b[j])/(double)(a[i]-a[j]);
sort(k+i+1,k+n+1,cmp);
ll cnt=1,lst=i;
for(ll kk=i+1;kk<=n;kk++){
if(abs(k[kk]-k[lst])<0.000000001) cnt++;
else{
lst=kk;
ans-=cnt*(cnt-1)/2;
cnt=1;
}
}
ans-=cnt*(cnt-1)/2;
}
printf("%lld\n",ans);
}
int main(){
freopen("triangle.in","r",stdin);
freopen("triangle.out","w",stdout);
init();
solve();
return 0;
}