【2019CCPC秦皇岛:A】Angle Beats(离线+斜率Hash+分类讨论)
题目地址:https://codeforces.com/gym/102361/problem/A
题目:
n个给定点,q个询问点,每次询问给出一个坐标A,问从n中选定两个点B,C,有多少种方案使得ABC是个直角三角形。
解题思路:
标称的思路是从极角考虑的,训练的时候我以为是从极角考虑这个问题的,但是atan2,再加上极角+-,误差太大,我第二个样例就挂了(技艺不精QAQ)。
离线做,只需要开一个unorded_map。分直角顶点是询问点和给定点两种。
按斜率来考虑就简单很多,两个垂直的线段一定满足斜率相乘=-1,但是为了避免误差不需要求出真正的斜率,将斜率看作一个二元组(x,y)(约分使得gcd(x,y)=1),那么和它垂直的直线斜率为(-y,x)或者(y,-x)。
用unorded_map<Point, int>编译器会出现提示错误,“Call to implicitly-deleted default constructor of 'unordered_map<Point, int>'”,我目前还不清楚怎么改,把这个二元组hash一下即可,注意特判水平线和垂直线。
ll Hash(Point v) { ll A = 2333, B = 5279, C = 998244353; return A*v.x + B*v.y + C; }
ac代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2000 + 10;
const double pi = acos(-1.0);
const double eps = 1e-8;
typedef unsigned long long ll;
struct Point{
int x, y;
Point(int x = 0, int y = 0):x(x),y(y){}
};
typedef Point Vector;
Vector operator - (Vector A, Vector B)
{
return Point(A.x-B.x, A.y-B.y);
}
bool operator == (Vector A, Vector B)
{
return A.x==B.x && A.y==B.y;
}
Point p[maxn], qur[maxn];
int ans[maxn]={0};
unordered_map<ll, int> m;
int gcd(int a, int b)
{
return b == 0 ? a : gcd(b, a%b);
}
Point getSlope(Point A, Point B)
{
Point v = A-B;
if(v.x == 0) return Point(0, 1);
if(v.y == 0) return Point(1, 0);
int com = gcd(v.x, v.y);
v.x/=com; v.y/=com;
return v;
}
ll Hash(Point v)
{
ll A = 2333, B = 5279, C = 998244353;
return A*v.x + B*v.y + C;
}
int main()
{
//freopen("/Users/zhangkanqi/Desktop/11.txt","r",stdin);
int n, q;
while(scanf("%d %d", &n, &q) == 2)
{
for (int i = 0; i < n; i++) scanf("%d %d", &p[i].x, &p[i].y);
for (int i = 0; i < q; i++) scanf("%d %d", &qur[i].x, &qur[i].y);
for (int i = 0; i < q; i++)//查询点是直角顶点
{
m.clear();
for(int j = 0; j < n; j++)
{
Point v = getSlope(p[j], qur[i]);
m[Hash(v)]++;
}
for(int j = 0; j < n; j++)
{
Point v = getSlope(p[j], qur[i]);
if(v == Point(0,1) && m.count(Hash(Point(1,0)))) ans[i] += m[Hash(Point(1,0))];
else if(v == Point(1,0) && m.count(Hash(Point(0,1)))) ans[i] += m[Hash(Point(0,1))];
else ans[i] += (m[Hash(Point(-v.y, v.x))] + m[Hash(Point(v.y, -v.x))]);
}
ans[i] /= 2;
}
for (int i = 0; i < n; i++)//给定点是直角顶底
{
m.clear();
for(int j = 0; j < n; j++)
{
if(i == j) continue;
Point v = getSlope(p[i], p[j]);
m[Hash(v)]++;
}
for(int j = 0; j < q; j++)
{
Point v = getSlope(p[i], qur[j]);
if(v == Point(0,1) && m.count(Hash(Point(1,0)))) ans[j] += m[Hash(Point(1,0))];
else if(v == Point(1,0) && m.count(Hash(Point(0,1)))) ans[j] += m[Hash(Point(0,1))];
else ans[j] += (m[Hash(Point(-v.y, v.x))] + m[Hash(Point(v.y, -v.x))]);
}
}
for (int i = 0; i < q; i++) printf("%d\n", ans[i]);
}
return 0;
}