HDU - 5784 How Many Triangles(计算锐角三角形的个数)

题目链接:点击查看

题目大意:给出 n 个点,计算有多少个锐角三角形

题目分析:直接枚举点计算的话不太好办,因为 n 给到了 2000 ,直接枚举的话复杂度到了 n^3 ,但正难则反,我们考虑一下锐角三角形是由三个锐角组成的,而钝角三角形和直角三角形是由两个锐角 + 一个非锐角组成的,所以我们可以先计算出所有锐角的个数 ans ,以及所有非锐角的个数 not_ans ,那么答案显然是 ( ans - not_ans * 2 ) / 3 了

这样问题就稍微简单一点了,我们只需要O(n)枚举第一个点,然后极角排序+双指针确定下来剩下的两个点,就能计算角的个数了,实现比较简单,需要注意细节就是 x 和 y 在这个题目中到了 1e9 的程度,而 atan( 1e9 , -1e9 ) 则更是到了1e-18的精确度,但也不至于这么极限,总之eps需要稍微开大点,大概开到 1e9 的100倍,也就是 1e-11 就足够了

2020.9.16更新:

关于这种统计三角形个数的题目,用atan来对极角排序的话,丢精度丢的太明显了,所以用叉积又写了一发

代码:

按照atan排序

#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<unordered_map>
using namespace std;
   
typedef long long LL;
  
typedef unsigned long long ull;
   
const int inf=0x3f3f3f3f;
   
const int N=2e3+100;

const double eps = 1e-11;

const double pi = acos(-1.0);

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

struct Point{
	double x,y;
	Point(){}
	Point(double _x,double _y){
		x = _x;
		y = _y;
	}
	void input(){
		scanf("%lf%lf",&x,&y);
	}
	Point operator -(const Point &b)const{
		return Point(x-b.x,y-b.y);
	}
}point[N];

vector<double>node;

int n;

int get_num(double angle)
{
	int r=0,ans=0;
	for(int l=0;l<n-1;l++)
	{
		int cnt=0;//计算有多少个共线的点
		while(sgn(node[l+cnt]-node[l])==0)
			cnt++;
		l+=cnt-1;//最后一个共线的点
		r=max(r,l+1);
		while(r-l+1<n&&sgn(node[r]-node[l]-angle)<0)
			r++;
		ans+=(r-l-1)*cnt;//注意点 r 是不满足要求的,所以答案是(r-1)-l个
	}
	return ans;
}

int main()
{
//#ifndef ONLINE_JUDGE
//  freopen("input.txt","r",stdin);
//    freopen("output.txt","w",stdout);
//#endif
//  ios::sync_with_stdio(false);
   	while(scanf("%d",&n)!=EOF)
   	{
   		LL ans=0,not_ans=0;
   		for(int i=1;i<=n;i++)
   			point[i].input();
   		for(int i=1;i<=n;i++)
   		{
   			node.clear();
   			for(int j=1;j<=n;j++)
   			{
   				if(i==j)
   					continue;
   				Point t=point[j]-point[i];
   				node.push_back(atan2(t.y,t.x));
			}
			sort(node.begin(),node.end());
			for(int i=0;i<n-1;i++)//令范围增加一倍,从[0,2*pi]到[0,4*pi]
				node.push_back(node[i]+2*pi);
			int temp=get_num(pi/2);//求出有多少个锐角 
			ans+=temp;
			not_ans+=get_num(pi)-temp;//求出有多少个直角/钝角 
		}
		printf("%lld\n",(ans-2*not_ans)/3);
	} 
   
   
   
    
      
      
      
      
      
      
      
      
      
      
    return 0;
}

按照叉积排序

//#pragma GCC optimize(2)
//#pragma GCC optimize("Ofast","inline","-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
#include<unordered_map>
#define double LL
using namespace std;
      
typedef long long LL;
      
typedef unsigned long long ull;
      
const int inf=0x3f3f3f3f;
    
const int N=2e3+100;//顶点数 
 
LL ans,not_ans;
 
int n;
 
int sgn(double x){
    if(x==0)return 0;
    if(x < 0)return -1;
    else return 1;
}
 
struct Point{
    double x,y;
    Point(){}
    Point(double _x,double _y){
        x = _x;
        y = _y;
    }
    void input(){
        scanf("%lld%lld",&x,&y);
    }
    bool operator == (Point b)const{
        return sgn(x-b.x) == 0 && sgn(y-b.y) == 0;
    }
    bool operator < (Point b)const{
        return sgn(x-b.x)== 0?sgn(y-b.y)<0:x<b.x;
    }
    Point operator -(const Point &b)const{
        return Point(x-b.x,y-b.y);
    }
    Point operator +(const Point &b)const{
        return Point(x+b.x,y+b.y);
    }
    //叉积
    double operator ^(const Point &b)const{
        return x*b.y - y*b.x;
    }
    //点积
    double operator *(const Point &b)const{
        return x*b.x + y*b.y;
    }
    //返回两点的距离
    double distance(Point p){
        return (x-p.x)*(x-p.x)+(y-p.y)*(y-p.y);
    }
}p[N],q[N<<1];

int getxx(Point& a) 
{
    if(a.x>0 && a.y>=0) return 1;
    if(a.x<=0 && a.y>0) return 2;
    if(a.x<0 && a.y<=0) return 3;
    if(a.x>=0 && a.y<0) return 4;
}

bool cmp(Point a,Point b)
{
	if(getxx(a)!=getxx(b))
		return getxx(a)<getxx(b);
	return sgn(a^b)>0;
}
 
void solve(int id)
{
    for(int i=0,j=0;i<n;i++)
        if(i!=id)
        	q[j++]=p[i]-p[id];
    sort(q,q+n-1,cmp);
    int n=::n-1;
    for(int i=0;i<n;i++)
        q[i+n]=q[i];
    int j=0,k=0,l=0;//相同极角的点的位置,锐角极角的位置,钝角极角的位置
    for(int i=0;i<n;i++)
    {
        while(j<i+n&&(q[i]^q[j])==0&&(q[i]*q[j])>0)
            j++;
        k=max(k,j);
        while(k<i+n&&(q[i]^q[k])>0&&(q[i]*q[k])>0)
            k++;
        l=max(l,k);
        while(l<i+n&&(q[i]^q[l])>0)
            l++;
        ans+=k-j;
        not_ans+=l-k;
    }
}
 
int main()
{
#ifndef ONLINE_JUDGE
//  freopen("data.in.txt","r",stdin);
//  freopen("data.out.txt","w",stdout);
#endif
//  ios::sync_with_stdio(false);
    while(scanf("%d",&n)!=EOF)
    {
        ans=0,not_ans=0;
        for(int i=0;i<n;i++)
            p[i].input();
        for(int i=0;i<n;i++)
        	solve(i);
        printf("%lld\n",(ans-2*not_ans)/3);
    }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
   return 0;
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Frozen_Guardian

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值