2019CCPC秦皇岛A Angle Beats

这篇博客探讨了一种算法优化方法,用于解决计算直角三角形点对的问题。博主首先介绍了两种策略:离线处理和使用神奇的map操作。离线处理包括考虑点作为直角顶点的情况,通过极角排序和遍历计算符合条件的点对。而map操作则通过重载小于号来对斜率进行分类,从而快速计算直角顶点的点对。博主分享了两种方法的实现代码,并指出在不同平台上的运行时间差异。文章旨在展示如何通过算法优化提高效率,尤其是在处理大量数据时。
摘要由CSDN通过智能技术生成

题意

给你n个点(p[]) 再给你m个查询每次一个点(A[])问你与当前查询点能构成直角三角形的点对有多少对? 题目多组

思路

好怪异啊hdu6731上给的15s gym102361上给的4s(好像比赛的时候就是4s)

大体思路:A作直角顶点 + A不作直角顶点

两种做法:

1.离线去做,

对于A作直角顶点的情况,每次从A[]里选择一个点作为极角排序的参照点对p[]排序 然后O(n)枚举与A相连边的i及此时有多少j满足∠iAj=90 复杂度q*n*logn

对于p作直角顶点的情况,每次从p[]中选一个参照点,对剩下的p[]和所有A[]极角排序 寻找答案 大题思路同上 代码比较不好写 注意需要正反跑两次因为90只算了一个方向的

但是这份代码只过了hdu的 gymT了 不知道是我写太挫还是多组问题这个算法实在过不去 因为已经是卡着4e8在跑了 gym上用下面map代码才过得qaq

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>

using namespace std;
const int MaxN = 2e3 + 5;
const double eps = 1e-8;
typedef long long LL;

int n,q,ans[MaxN];
struct Point{
	LL x,y;
	int id;
	Point(LL x = 0,LL y = 0,int id = 0):x(x),y(y),id(id){}
}C,p[MaxN],A[MaxN],B[MaxN << 1];//C-极角坐标的参照点
typedef Point Vector;

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

Vector operator + (Vector A,Vector B){ return Vector(A.x+B.x,A.y+B.y);}
Vector operator - (Vector A,Vector B){ return Vector(A.x-B.x,A.y-B.y);}
Vector operator * (Vector A,double p){ return Vector(A.x*p,A.y*p);}
bool operator == (const Point &a,const Point &b){
	return dcmp(a.x-b.x) == 0 && dcmp(a.y-b.y) == 0;
}

LL Dot(Vector A,Vector B){ return A.x*B.x + A.y*B.y;}
LL Cross(Vector A,Vector B){ return A.x*B.y-A.y*B.x;}

LL dis(Point A,Point B){
	return (A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y);
}

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

LL compare(Point a,Point b,Point c){
	return Cross(c - a,c - b);
}
bool cmp1(Point a,Point b){//费时间but精度高
	if(compare(a,b,C) == 0) return a.x < b.x;
	else return compare(a,b,C) > 0;
}
bool cmp(Point a,Point b){
	if(Quadrant(a - C) == Quadrant(b - C)) return cmp1(a,b);
	else return Quadrant(a - C) < Quadrant(b - C);
}

int main()
{
	while(~scanf("%d %d",&n,&q)){
		memset(ans,0,sizeof ans);
		for(int i = 1;i <= n; i++){
			scanf("%lld %lld",&p[i].x,&p[i].y);
			p[i].id = -1;
		}
		for(int i = 1;i <= q; i++){
			scanf("%lld %lld",&A[i].x,&A[i].y);
			A[i].id = i;
		}
		//A作直角顶点 q*nlog(n)
		for(int k = 1;k <= q; k++){
			C = A[k];
			sort(p + 1,p + 1 + n,cmp);
			int pre = 0;//上一个答案(共线可继承)
			for(int i = 1,j = 1;i <= n; i++){
				if(i > 1 && Cross(p[i] - C,p[i - 1] - C) == 0 && Dot(p[i] - C,p[i - 1] - C) > 0){
					ans[k] += pre;
					continue;
				}
				pre = 0;
				while(Cross(p[i] - C,p[j] - C) >= 0 && Dot(p[i] - C,p[j] - C) >= 0){
					//<=90内逐渐逼近 >90不用管
					if(Cross(p[i] - C,p[j] - C) > 0 && Dot(p[i] - C,p[j] - C) == 0){
						++pre,ans[k]++;
					}
					j = j % n + 1;
					if(i == j) break;
				}
			}
		}
		//p坐直角顶点
		for(int k = 1;k <= n; k++){
			C = p[k];
			int cnt = 0;
			for(int i = 1;i <= q; i++) B[++cnt] = A[i];
			for(int i = 1;i <= n; i++){
				if(i != k) B[++cnt] = p[i];
			}
			sort(B + 1,B + 1 + cnt,cmp);
			int pre = 0;
			for(int i = 1,j = 1;i <= cnt; i++){
				if(i > 1 && Cross(B[i] - C,B[i - 1] - C) == 0 && Dot(B[i] - C,B[i - 1] - C) > 0){
					if(B[i].id != -1){
						ans[B[i].id] += pre;
					}
					continue;
				}
				pre = 0;
				while(Cross(B[i] - C,B[j] - C) >= 0 && Dot(B[i] - C,B[j] - C) >= 0){
					//<=90内逐渐逼近 >90不用管
					if(B[j].id == -1 && Cross(B[i] - C,B[j] - C) > 0 && Dot(B[i] - C,B[j] - C) == 0){
						++pre;
						if(B[i].id != -1){
							ans[B[i].id]++;
						}
					}
					j = j % cnt + 1;
					if(i == j) break;
				}
			}
			for(int i = cnt,j = cnt;i >= 1; i--){
				if(i < cnt && Cross(B[i] - C,B[i + 1] - C) == 0 && Dot(B[i] - C,B[i + 1] - C) > 0){
					if(B[i].id != -1) ans[B[i].id] += pre;
					continue;
				}
				pre = 0;
				while(Cross(B[j] - C,B[i] - C) >= 0 && Dot(B[j] - C,B[i] - C) >= 0){
					if(B[j].id == -1 && Cross(B[j] - C,B[i] - C) > 0 && Dot(B[j] - C,B[i] - C) == 0){
						++pre;
						if(B[i].id != -1) ans[B[i].id]++;
					}
					if(j == 1) j = cnt;
					else j--;
					if(i == j) break;
				}
			}
		}
		for(int i = 1;i <= q; i++) printf("%d\n",ans[i]);
	}
	return 0;
}

2.神奇map操作比上面那个快多了

还是分两组情况 每次直接看与当前垂直的线段有没有有多少更新ans[]

map通过重载小于号实现对斜率的分类 哇是真的没听过

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<map>

using namespace std;
const int MaxN = 2e3 + 5;
const double eps = 1e-8;
typedef long long LL;

int n,q,ans[MaxN];
struct Point{
	LL x,y;
	Point(LL x = 0,LL y = 0):x(x),y(y){}
	Point Base() const{
		if(x < 0 || (x == 0 && y < 0)) return Point(-x,-y);
		return *this;
	}
	bool operator < (const Point &qaq)const{
		Point P1 = Base(),P2 = qaq.Base();
		return P1.y * P2.x < P2.y * P1.x;
	}
	void input(){
		scanf("%lld %lld",&x,&y);
	}
}p[MaxN],A[MaxN];
typedef Point Vector;

Vector operator + (Vector A,Vector B){ return Vector(A.x+B.x,A.y+B.y);}
Vector operator - (Vector A,Vector B){ return Vector(A.x-B.x,A.y-B.y);}
Vector operator * (Vector A,double p){ return Vector(A.x*p,A.y*p);}

map<Point,int> mp;

int main()
{
	int n,q;
	scanf("%d %d",&n,&q);
	for(int i = 1;i <= n; i++) p[i].input();
	for(int i = 1;i <= q; i++){
		A[i].input();
		mp.clear();
		int cur = 0;
		for(int j = 1;j <= n; j++){
			Point now = A[i] - p[j];
			mp[now]++;
		}
		map<Point,int> :: iterator it;
		for(it = mp.begin();it != mp.end(); it++){
			Point now;
			now.x = -1 * it->first.y,now.y = it->first.x;
			if(mp.count(now)) cur += it->second * mp[now];
		}//以A为直角顶点
		ans[i] += cur / 2;
	}

	for(int i = 1;i <= n; i++){
		mp.clear();
		for(int j = 1;j <= n; j++){
			if(i == j) continue;
			mp[p[j] - p[i]]++;
		}//存p[i]~[j]
		for(int j = 1;j <= q; j++){
			Point now = A[j] - p[i];
			now = Point(-1 * now.y,now.x);//与之垂直的
			if(mp[now]) ans[j] += mp[now];
		}
	}
	for(int i = 1;i <= q; i++) printf("%d\n",ans[i]);
	return 0;
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值