HDU 5784 How Many Triangles

计算几何,极角排序,双指针,二分。

直接找锐角三角形的个数不好找,可以通过反面来求解。

首先,$n$个点最多能组成三角形个数有$C_n^3$个,但是这之中还包括了直角三角形,钝角三角形,平角三角形,我们需要减去这些三角形的个数。

如果在$n$个点中找到了$A$个直角,那么必然有$A$个直角三角形。

同理,如果找到了$B$个钝角,那么必然有$B$个钝角三角形。

同理,如果找到了$C$个平角,那么必然有$C$个平角三角形。

那么答案:$ans=C_n^3-A-B-C$。

接下里的任务就是求解$A$,$B$,$C$的总和是多少。

计算$A+B+C$,可以枚举一个点$P$作为三角形顶点,然后剩余的点根据$P$点作为原点进行极角排序,然后进行尺取(或者二分)。

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<iostream>
using namespace std;
typedef long long LL;
const double pi=acos(-1.0),eps=1e-8;
void File()
{
    freopen("D:\\in.txt","r",stdin);
    freopen("D:\\out.txt","w",stdout);
}
template <class T>
inline void read(T &x)
{
    char c = getchar(); x = 0;while(!isdigit(c)) c = getchar();
    while(isdigit(c)) { x = x * 10 + c - '0'; c = getchar();  }
}

const int MAX = 2010;
struct point{ LL x, y; }s[MAX],t[MAX];
int n;

LL CrossProd(point p0, point p1, point p2){
   return (p1.x-p0.x)*(p2.y-p0.y) - (p1.y-p0.y)*(p2.x-p0.x);
}

bool cmp(const point &a, const point &b)
{
    if (a.y == 0 && b.y == 0 && a.x*b.x <= 0) return a.x>b.x;
    if (a.y == 0 && a.x >= 0 && b.y != 0) return true;
    if (b.y == 0 && b.x >= 0 && a.y != 0) return false;
    if (b.y*a.y <= 0) return a.y>b.y;
    point one;  one.y = one.x = 0;
    return CrossProd(one,a,b) > 0 || (CrossProd(one,a,b) == 0 && a.x < b.x);
}

LL dis2(point a,point b)
{
    return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y);
}

bool check(point a,point b,point c)
{
    LL lena=dis2(b,c);
    LL lenb=dis2(a,c);
    LL lenc=dis2(a,b);
    if(lenb+lenc-lena<=0) return 1;
    return 0;
}

int main()
{
    while(~scanf("%d",&n))
    {
        for(int i=1;i<=n;i++) scanf("%lld%lld",&t[i].x,&t[i].y);
        LL ans=(LL)n*(LL)(n-1)*(LL)(n-2)/(LL)6;

        int sz;
        for(int i=1;i<=n;i++)
        {
            sz=0;
            for(int j=1;j<=n;j++)
            {
                if(i==j) continue;
                s[sz].x=t[j].x-t[i].x; s[sz].y=t[j].y-t[i].y;
                sz++;
            }

            sort(s,s+sz,cmp);

            for(int j=0;j<sz;j++)
            {
                point tmp; tmp.x=0; tmp.y=0;
                int L=j,R=sz-1,pos=-1;
                while(L<=R)
                {
                    int mid=(L+R)/2;
                    if(CrossProd(tmp,s[j],s[mid])>=0) L=mid+1,pos=mid;
                    else R=mid-1;
                }

                if(pos!=-1&&pos>=j+1)
                {
                    L=j+1,R=pos; int h=-1;
                    while(L<=R)
                    {
                        int mid=(L+R)/2;
                        if(check(tmp,s[j],s[mid])) R=mid-1, h=mid;
                        else L=mid+1;
                    }

                    if(h!=-1) ans=ans-(LL)(pos-h+1);
                }

                if(pos!=-1&&pos+1<=sz-1)
                {
                    L=pos+1,R=sz-1; int h=-1;
                    while(L<=R)
                    {
                        int mid=(L+R)/2;
                        if(check(tmp,s[j],s[mid])) L=mid+1, h=mid;
                        else R=mid-1;
                    }

                    if(h!=-1) ans=ans-(LL)(h-pos);
                }
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/zufezzt/p/5797966.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值