Hdu 5784 How Many Triangles(极角排序+尺取法)

本文介绍了如何利用极角排序和尺取法解决HDU 5784问题,通过计算锐角、直角和钝角的数量来确定锐角三角形的个数。具体方法包括按极角对向量排序,使用三个指针l、r、equ分别表示直角、平角和非0角的位置,并通过枚举向量计算以特定点为顶点的锐角和钝角个数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=5784

思路:

1.锐角三角形总锐角个数=总锐角数-非锐角三角形提供锐角数。则锐角三角形个数=总锐角数/3(即(锐角数-2*(直角+钝角数))/3,每钝角和直角三角形提供两锐角)。

2.枚举每个点p[i],以p[i]为原点,求其他n-1个点与原点组成的向量,按极角(小于0时加2*PI)递增排序。设置三个指针l、r、equ,分别代表第一个直角、第一个平角、第一个非0角的位置。枚举n-1个向量j,由于极角递增,两向量角度即为极角l-极角j,l、r、equ递增则形成的角度也递增(相当于尺取法)。则以点p[i]为顶点的锐角个数为l-equ(所有小于90度的角减去0度的角),钝角个数为r-l(所有小于180度的角减去小于等于90度的角)。

3.当指针l、r、equ旋转一周时(l、r、equ下标超过n-1,但此时所有角度尚未枚举完毕),为了便于求角度(即最后一部分向量与起始部分向量所成角度),枚举之前将所有极角加2*PI复制一遍。

#include<cmath>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#define debug
using namespace std;

const double eps=1e-11;
const double PI=acos(-1.0);
const int maxn=4000+50;

int n;

struct Point
{
    double x,y;
    Point() {}
    Point(double x,double y):x(x),y(y) {}
    void read()
    {
        scanf("%lf%lf",&x,&y);
    }
};

typedef Point Vector;

Vector operator - (Vector A,Vector B)
{
    return Vector(A.x-B.x,A.y-B.y);
}

int dcmp(double x)
{
    if(fabs(x)<eps) return 0;
    else return x<0?-1:1;
}

int cnt;
Point p[maxn];
double a[maxn];

int main()
{
#ifdef debu
    freopen("in.txt","r",stdin);
#endif // debug
    while(scanf("%d",&n)!=EOF)
    {
        int acute=0,notacute=0;
        for(int i=1; i<=n; i++) p[i].read();
        for(int i=1; i<=n; i++)
        {
            cnt=0;
            for(int j=1; j<=n; j++)
            {
                if(i==j)continue;
                Vector tmp=p[j]-p[i];
                double ang=atan2(tmp.y,tmp.x);
                if(ang<0) ang+=2*PI;
                a[++cnt]=ang;
            }

            sort(a+1,a+cnt+1);

            for(int j=1;j<=cnt;j++) a[cnt+j]=a[j]+2*PI;

            int l=1,r=1,equ=1;

            for(int j=1; j<=cnt; j++)
            {
                while(l<=2*cnt&&dcmp(a[l]-a[j]-PI/2)<0) l++;
                while(r<=2*cnt&&dcmp(a[r]-a[j]-PI)<0) r++;
                while(equ<=2*cnt&&dcmp(a[equ]-a[j])==0) equ++;


                acute+=l-equ;
                notacute+=r-l;
            }
        }

        printf("%d\n",(acute-2*notacute)/3);
    }
    return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值