[solution] JZOJ3493 三角形

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

猜你喜欢

转载自www.cnblogs.com/theOldChun/p/9458418.html
今日推荐