CDOJ 1155 统计锐角三角形个数

题意:二维平面给n个点(可能有重点) Pi(1in) ,任取 i,j,k(1i<j<kn) ,问有多少对 (i,j,k) 能使得 Pi,Pj,Pk 组成锐角三角形。

能组成锐角三角形的条件为: Pi,Pj,Pk 互不相同,且三个角分别为锐角(大于0,小于90度)

解法:

最优解复杂度:O(n * n * log(n * n))
方法:用容斥的方法,先统计所有三角形个数,然后减去钝角和直角的,钝角和直角很好统计,枚举每个点,再枚举每个点,得到的向量极角排序,然后二分查找即可。。注意对重点的处理。。其实还可以用二指针来做,不过写起来比较麻烦

暴力复杂度:O(n * n * n)
暴力方法:枚举三个点,看能否构成锐角三角形,可以的条件有;1.没有重点;2.三个角都是锐角。判断2的方法:两个点中点是圆心,距离一般是半径,作圆,第三个点在圆内则该角是锐角。注意要判断三个角都是锐角才可。

Code

//Hello. I'm Peter.
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;

const double eps = 1e-9, pi = acos(-1.0);
inline int sgn(double x){
    if(fabs(x) < eps) return 0;
    else return x > 0 ? 1 : -1;
}

#define N 100010
int n;
struct Point{
    double x, y;
    double rad;
    Point(){};
    Point(double x1, double y1){
        x = x1, y = y1;
    }
}p[N],v[N];
int nv;
typedef Point Vector;
Vector operator - (Vector a, Vector b){
    return Vector(a.x - b.x, a.y - b.y);
}
bool cmp1(const Point a, const Point b){
    return sgn(a.rad - b.rad) < 0;
}
bool cmp2(const Point a, const Point b){
    if(sgn(a.x - b.x) != 0) return a.x < b.x;
    else return a.y < b.y;
}

int lowbou(double x){
    int l = 0, r = nv, mid;
    while(l < r){
        mid = (l + r) >> 1;
        if(sgn(v[mid].rad - x) < 0) l = mid + 1;
        else r = mid;
    }
    return l;
}
int upbou(double x){
    int l = 0, r = nv, mid;
    while(l < r){
        mid = (l + r) >> 1;
        if(sgn(v[mid].rad - x) <= 0) l = mid + 1;
        else r = mid;
    }
    return l;
}

ll ans;
void solve(){
    sort(v, v + nv, cmp1);
    for(int i = 0; i < nv; i++){
        double a1 = v[i].rad - pi/2;
        double a2 = v[i].rad - pi;
        if(sgn(a1) < 0) a1 += 2 * pi;
        if(sgn(a2) < 0) a2 += 2 * pi;
        if(sgn(a1 - a2) >= 0)
        {
            int k1 = upbou(a1) - 1;
            int n1 = k1 + 1;
            int k2 = upbou(a2) - 1;
            int n2 = k2 + 1;
            ans -= n1 - n2;
        }
        else
        {
            int k1 = upbou(a1) - 1;
            int n1 = k1 + 1;
            int k2 = upbou(a2);
            int n2 = nv - k2;
            ans -= n1 + n2;
        }
    }

    ll ans1 = 0;
    for(int i = 0; i < nv; i++){
        double a = v[i].rad - pi;
        if(sgn(a) < 0) a += 2 * pi;
        int k1 = lowbou(a);
        int k2 = upbou(a) - 1;
        ans1 += k2 - k1 + 1;
    }
    ans -= ans1 >> 1;
}

int main(){
    scanf("%d",&n);
    for(int i = 0; i < n; i++){
        scanf("%lf%lf",&p[i].x,&p[i].y);
    }
    ans = 1LL * n * (n-1) * (n-2) / 6;
    sort(p, p + n, cmp2);
    for(int i = 0; i < n;){
        int j = i;
        for(; sgn(p[i].x - p[j].x) == 0 && sgn(p[i].y - p[j].y) == 0; j++);
        j--;
        int n1 = j - i + 1;
        ans -= 1LL * n1 * (n1 - 1) * (n1 - 2) / 6;
        ans -= (1LL * n1 * (n1 - 1) / 2) * (n - n1);
        i = j + 1;
    }

    for(int i = 0; i < n; i++){
        nv = 0;
        for(int j = 0; j < n; j++){
            if(sgn(p[i].x - p[j].x) == 0 && sgn(p[i].y - p[j].y) == 0) continue;
            v[nv] = p[j] - p[i];
            v[nv].rad = atan2(v[nv].y, v[nv].x);
            if(sgn(v[nv].rad) < 0) v[nv].rad += 2 * pi;
            nv++;
        }
        if(nv == 0) continue;
        solve();
    }

    printf("%lld\n",ans);
    return 0;
}

你可能感兴趣的:(CDOJ 1155 统计锐角三角形个数)