Gym - 102361A Angle Beats(几何)

题目链接:点击查看

题目大意:给出n个点,再给出m个询问,每次询问给出一个点 x,我们需要回答包括点 x 的直角三角形有多少个

题目分析:题目比较直接,数据也比较小,支持n*n的算法处理,首先我们必须知道,如果要包括点 x 所组成三角形,那么点 x 可以在直角点上,也可以在非直角点上,比较容易想到的方法是离线处理,我们需要分类讨论,也就是上述两个情况,对于每个询问而言,以点 x 为相对原点,对其余n个点进行极角排序,然后用双指针保证时间复杂度为O(n),一个指针固定住一条直角边,另一个指针去找另一条直角边上有多少个点,这样时间复杂度是O(n*m),解决了点 x 在直角点上的答案,剩下非直角点上的答案我们可以O(n)枚举直角点,同上利用双指针计算贡献,对于有贡献的点 x 累加答案即可,时间复杂度为大概就是n*m*logn的样子,不过实现起来比较麻烦,不想多说

还有一种方法比较简单,但是不太好想,还比较考察代码功底,就是围绕着map展开,首先我们要找直角三角形,本质上是要找垂直的两条边,换句话说就是需要找两个向量垂直,将点抽象成向量就简单多了,和上面大同小异,当点 x 为直角点时,我们记录下点 x 与 n 个点的向量,最后跑一遍map统计答案即可,当点 x 为非直角点时,一样O(n)枚举直角点,O(n)枚举另一个非直角点,然后直接统计答案就好了,实现起来比较简单,不过难点不是main函数里,而是在Point结构体内对小于号的重载,因为我们需要的是向量,也就是说需要一个带斜率的直线,为了方便起见,我们在重载小于号时应该试图将其归类,譬如(2,1)和(4,2)虽然数值不一样,但是代表的都是同一个向量,我们应该将其归为一类而不是两类,第一次知道map竟然可以根据小于号将载入的数据重新分类,有了这样一个方便的性质,在重载好小于号后就可以直接实现main函数里的内容了,比较简单,具体实现看代码

代码:

#include<iostream>
#include<cstdio> 
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<sstream>
using namespace std;
 
typedef long long LL;
 
const int inf=0x3f3f3f3f;

const int N=2e3+100;

struct Point{
	LL x,y;
	Point(){}
	Point(int _x,int _y){
		x = _x;
		y = _y;
	}
	void input(){
		scanf("%lld%lld",&x,&y);
	}
	Point change()const//将所有的向量都转换为非负数
	{
		if(x<0||x==0&&y<0)
			return Point(-x,-y);
		return *this;
	}
	bool operator < (const Point &b)const{
		Point t1=change(),t2=b.change();
		return t1.x*t2.y<t1.y*t2.x;//利用斜率在map中分类,如果斜率相同则属于同一个向量
	}
	Point operator -(const Point &b)const{
		return Point(x-b.x,y-b.y);
	}
}point[N];

map<Point,int>mp;

vector<Point>node;

int ans[N];

int main()
{
//	freopen("input.txt","r",stdin);
//	ios::sync_with_stdio(false);
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
		point[i].input();
 	for(int i=0;i<m;i++)//查询点为直角点 
 	{
 		mp.clear();
 		Point temp;
 		temp.input();
 		node.push_back(temp);
 		for(int i=1;i<=n;i++)
 			mp[point[i]-temp]++;
 		for(auto it:mp)
 		{
 			Point temp(-it.first.y,it.first.x);
 			ans[i]+=mp[temp]*it.second;
		}
		ans[i]/=2;//因为答案累加了两次,需要除以二
	}
	for(int i=1;i<=n;i++)//枚举n个点分别作为直角点
	{
		mp.clear();
		for(int j=1;j<=n;j++)//枚举另一个点 
		{
			if(i!=j)
				mp[point[j]-point[i]]++;
		}
		for(int j=0;j<m;j++)
		{
			Point temp=node[j]-point[i];
			temp=Point(-temp.y,temp.x);
			ans[j]+=mp[temp];
		}
	} 
	for(int i=0;i<m;i++)
		printf("%d\n",ans[i]);
 
 
 
 
 
	
	
	
	
	
	
	
	
	
	return 0;
}
发布了577 篇原创文章 · 获赞 18 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_45458915/article/details/104103973
今日推荐