题意
给你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;
}