代码中的板子参考了繁凡和俊杰带佬的板子
二维基础
A.计算几何spj hacker
题意:
输入一组数a,b使无论ans为任何数,以下函数都返回AC。
// a and b are outputed by the contestant
// ans is provided by the jury
int check(int a,int b,double ans)
{
double t=(double)a/(double)b;
if(fabs(t-ans)>1e-9)
return WA;
return AC;
}
题解:
当a/b为nan值时,返回True。
代码:
//php
0 0
B.Winding Number
题意:
牛妹沿着 n n n个点走,询问牛牛在某点观察到牛妹逆时针走的圈数与顺时针的圈数之差。
题解:
回转数板子
代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
typedef pair<bool, int>Pr;
//constexpr double eps = 1e-8;
constexpr long long eps=0;
constexpr double PI = 3.1415926535897932384l;
template<typename T> struct point
{
T x, y;
bool operator==(const point& a) const { return (abs(x - a.x) <= eps && abs(y - a.y) <= eps); }
bool operator<(const point& a) const { if (abs(x - a.x) <= eps) return y < a.y - eps; return x < a.x - eps; }
point operator+(const point& a) const { return { x + a.x,y + a.y }; }
point operator-(const point& a) const { return { x - a.x,y - a.y }; }
point operator-() const { return { -x,-y }; }
point operator*(const T k) const { return { k * x,k * y }; }
point operator/(const T k) const { return { x / k,y / k }; }
T operator*(const point& a) const { return x * a.x + y * a.y; } //点乘 Dot
T operator^(const point& a) const { return x * a.y - y * a.x; } //叉乘 Cross
// 1:左 -1:右 0:线上
//auto的原理就是根据后面的值,来自己推测前面的类型是什么。
//return *this返回当前对象, return this返回当前对象的地址(指向当前对象的指针)
int toleft(const point& a) const { const auto t = (*this) ^ a; return (t > eps) - (t < -eps); }
T len2() const { return (*this) * (*this); }
T dis2(const point& a) const { return (a - (*this)).len2(); }
double len() const { return sqrt(len2()); }
double dis(const point& a) const { return sqrt(dis2(a)); }
//夹角
double ang(const point& a) const { return acos(max(-1.0, min(1.0, ((*this) * a) / (len() * a.len())))); }
//逆时针旋转rad
point rot(const double rad) const { return { x * cos(rad) - y * sin(rad),x * sin(rad) + y * cos(rad) }; }
};
using Point = point<ll>;
template<typename T> struct line
{
point<T> p, v;
bool operator==(const line& a) const { return v.toleft(a.v) == 0 && v.toleft(p - a.p) == 0; }
int toleft(const point<T>& a) const { return v.toleft(a - p); }
//直线交点
point<T> inter(const line& a) const { return p + v * ((a.v ^ (p - a.p)) / (v ^ a.v)); }
//点到直线的距离
double dis(const point<T>& a) const { return abs(v ^ (a - p)) / v.len(); }
//a在直线上的投影点
point<T> proj(const point<T>& a) const { return p + v * ((v * (a - p)) / (v * v)); }
};
using Line = line<ll>;
template<typename T> struct polygon
{
vector<Point> p;
//返回下一个点和前一个点
//size_t可理解为unsigned int,应为数组下标类型
inline size_t nxt(const size_t i) const { return i == p.size() - 1 ? 0 : i + 1; }
inline size_t pre(const size_t i) const { return i == 0 ? p.size() - 1 : i - 1; }
pair<bool, int> winding(const Point& a) const
{
int cnt = 0;
for (size_t i = 0; i < p.size(); i++)
{
Point u = p[i], v = p[nxt(i)];
if (abs((a - u) ^ (a - v)) <= eps && (a - u) * (a - v) <= eps) return { true,0 };//点在边上
if (abs(u.y - v.y) <= eps) continue;
Line uv = { u,v - u };
if (u.y < v.y - eps && uv.toleft(a) <= 0) continue;//该射线与边无交点
if (u.y > v.y + eps && uv.toleft(a) >= 0) continue;
//计算wn数
if (u.y < a.y - eps && v.y >= a.y - eps) cnt++;
if (u.y >= a.y - eps && v.y < a.y - eps) cnt--;
}
return { false,cnt };
}
//边长
double circ() const
{
double sum = 0;
for (size_t i = 0; i < p.size(); i++) sum += p[i].dis(p[nxt(i)]);
return sum;
}
T area2() const
{
T sum = 0;
for (size_t i = 0; i < p.size(); i++) sum += p[i] ^ p[nxt(i)];
return abs(sum);
}
};
using Polygon = polygon<ll>;
int n, m;
int main() {
cin >> n;
Polygon P;
for (int i = 0; i < n; i++) {
int x, y; cin >> x >> y;
P.p.push_back({ x,y });
}
cin >> m;
while (m--) {
int sx, sy; cin >> sx >> sy;
Pr S = P.winding({ sx,sy });
if (S.first)cout << "EDGE\n";
else cout << S.second << "\n";
}
return 0;
}
C.Mr. Main and Windmills
代码:
#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
#include<bits/stdc++.h>
#define db double
using namespace std;
const int maxn = 1e3 + 10;
const int inf = 1e9;
double nx[maxn], ny[maxn];
int sx, sy, tx, ty;
int N, M;
struct Point {
double x, y;
Point(double x = 0, double y = 0) :x(x), y(y) {}
};
vector<Point>vec[maxn];
vector<Point>vec1[maxn];
typedef Point Vector;
Vector operator + (Vector A, Vector B) {
return Vector(A.x + B.x, A.y + B.y);
}
Vector operator - (Point A, Point 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);
}
Vector operator / (Vector A, double p) {
return Vector(A.x / p, A.y / p);
}
struct Line {//直线定义
Point v, p;
Line(Point v, Point p) :v(v), p(p) {}
Point point(double t) {//返回点P = v + (p - v)*t
return v + (p - v) * t;
}
};
double Cross(Vector A, Vector B) {
return A.x * B.y - A.y * B.x;
}
Point GetLineIntersection(Point P, Vector v, Point Q, Vector w) {
Vector u = P - Q;
double t = Cross(w, u) / Cross(v, w);
return P + v * t;
}
double Dot(Vector A, Vector B) {
return A.x * B.x + A.y * B.y;
}
const double eps = 1e-15;
int dcmp(double x, double y) {
if (fabs(x - y) < eps)
return 0;
if (x > y)
return 1;
return -1;
}
int sgn(double x) {
if (fabs(x) < eps)
return 0;
if (x < 0)
return -1;
return 1;
}
bool OnSegment(Point p, Point a1, Point a2) {
return sgn(Dot(a1 - p, a2 - p)) < 0 ;//&& sgn(Cross(a1 - p, a2 - p)) == 0;
}
bool up(Point a, Point b) {
return a.x > b.x;
}
bool low(Point a, Point b) {
return a.x < b.x;
}
int main() {
cin >> N >> M;
cin >> sx >> sy >> tx >> ty;
Point S(sx, sy);
Point T(tx, ty);
Point P[maxn];
Line car(S, T);
for (int i = 1; i <= N; i++) {
cin >> P[i].x >> P[i].y;
}
int f = 0;
if (sx > tx)f = 1;
for (int i = 1; i <= N; i++) {
for (int j = 1; j <= N; j++) {
Line feng(P[i], P[j]);
if (i == j)continue;
if (Cross(S - T, P[i] - P[j]) == 0)continue;
Point d = GetLineIntersection(S, T - S, P[i], P[j] - P[i]);
if (OnSegment(d, S, T))vec[i].push_back(d);
}
int len = vec[i].size();
if (f)sort(vec[i].begin(), vec[i].end(), up);
else sort(vec[i].begin(), vec[i].end(), low);
}
while (M--) {
int p, k; cin >> p >> k;
int tt=vec[p].size();
if (tt < k)cout << -1 << endl;
else printf("%.10f %.10f\n", vec[p][k - 1].x, vec[p][k - 1].y);
}
return 0;
}
E.Operation Love
题意:
在二维坐标图给出一个手掌状图形,判断该图形是左手还是右手。
题解:
枚举所有线段,通过线段长度找到A,B,C三个点,若 B C ⃗ \vec{BC} BC在 A B ⃗ \vec{AB} AB的逆时针方向为左手,否则为右手。
代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const double eps = 1e-6;
struct Point {
double x, y;
Point(double x = 0, double y = 0) :x(x), y(y) {}
};
int sgn(double x) {
if (fabs(x) < eps)return 0;
if (x < 0)return -1;
return 1;
}
typedef Point Vector;
Vector operator + (Vector A, Vector B) { return Vector(A.x + B.x, A.y + B.y); }
Vector operator - (Point A, Point 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); }
Vector operator / (Vector A, double p) { return Vector(A.x / p, A.y / p); }
bool operator < (const Point& a, const Point& b) { return a.x < b.x || (a.x == b.x && a.y < b.y); }
bool operator ==(const Point& a, const Point& b) { return !sgn(a.x - b.x) && !sgn(a.y - b.y); }
//点积,可用于判断角度
double Dot(Vector A, Vector B) { return A.x * B.x + A.y * B.y; }
//叉积
double Cross(Vector A, Vector B) { return A.x * B.y - B.x * A.y; }
//模长
double Length(Vector A) { return sqrt(Dot(A, A)); }
//判断 bc 是不是在 ab 的逆时针方向,向量夹角<90
bool ToLeftTest(Point a, Point b, Point c) { return Cross(b - a, c - b) > 0; }
int T;
int main() {
ios::sync_with_stdio(0);
cin >> T;
while (T--) {
Point p[30], P1, P2, P3, P4;
for (int i = 1; i <= 20; i++)cin >> p[i].x >> p[i].y;
p[21] = p[1], p[22] = p[2];
p[0] = p[20];
for (int i = 1; i <= 20; i++) {
double L = Length(p[i + 1] - p[i]);
if (L - 8.5 > eps) {
// P1 = p[i], P2 = p[i + 1];
double L1 = Length(p[i - 1] - p[i]), L2 = Length(p[i + 2] - p[i + 1]);
if (L2 - L1 > eps)P1 = p[i], P2 = p[i + 1], P3 = p[i - 1];
if (L1 - L2 > eps)P1 = p[i + 1], P2 = p[i], P3 = p[i + 2];
break;
}
}
if (!ToLeftTest(P2, P1, P3))cout << "right\n";
else cout << "left\n";
}
return 0;
}
F.三角碰撞
题意
在一个三角形内,小球具有一个起始位置,方向及速度,遇到三角形的边会发生反弹,询问反弹 K 次的时间。
题解
大部分的反射问题都可以通过,将扩大延长原有图像的方法解决。
如图,我们可以把问题转化成一条具有方向的射线,需要多长时间,刚好与图中的三种直线有 k 个交点。
我们先看第一种直线。
我们只需要二分时间,得到该射线的终点,计算该条线段在y轴上的投影是三角形的高的几倍(红线与X轴之间蓝线的数量),倍数即是交点数。
对于第二,三种直线,我们需要通过坐标系变换,得到新的坐标,可通过第一种直线的方法解决。
坐标系变换,即是将X,Y轴逆时针旋转120°和240°,变换公式如下
[ cos θ − sin θ sin θ cos θ ] [ a x a y ] = [ cos θ a x − sin θ a y sin θ a x + c o s θ a y ] \begin{bmatrix} \cos\theta&-\sin\theta\\ \sin\theta&\cos\theta\\ \end{bmatrix} \begin{bmatrix} a_x\\ a_y\\ \end{bmatrix}= \begin{bmatrix} \cos\theta a_x-\sin\theta a_y\\ \sin\theta a_x+cos\theta a_y\\ \end{bmatrix} [cosθsinθ−sinθcosθ][axay]=[cosθax−sinθaysinθax+cosθay]
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const double eps = 1e-5;
const double gen3 = 1.7320508075688772935;
int T;
double L, x, y, vx, vy, k;
bool ok(double len) {
double res = 0, d = gen3 * L / 2.0;
res += abs(floor((y + (len * vy)) / d));
double py = fabs((-y - gen3 * x + d) / 2), pvy = (-vy - gen3 * vx) / 2;
res+= abs(floor((py + (len * pvy)) / d));
py = fabs((-y + gen3 * x + d) / 2), pvy = (-vy + gen3 * vx) / 2;
res += abs(floor((py + (len * pvy)) / d));
return res >= k;
}
int main() {
cin >> T;
while (T--) {
cin >> L >> x >> y >> vx >> vy >> k;
double lb = 0, rb = 1e11;
double mid = 0;
while (rb - lb > eps) {
mid = (rb + lb) / 2;
if (ok(mid))rb = mid;
else lb = mid;
}
printf("%.10f\n",mid);
}
return 0;
}
G.Magic Rabbit
题意
有三种体积相同,含有X溶液$ a_i ml/l$,Y溶液 b i m l / l b_i ml/l biml/l的混合溶液,询问能否得到题目给出的不同浓度的混合溶液。
题解
我们在坐标系中将点描出并连起来,会发现这个其实是一个线性规划问题,坐标系中围成的区域就是能够配出的溶液配比。
因此,这道题可以转化成求某点是否在三角形内。
特别的是,这个三角形可能是退化的三角形。
我们需要通过 to_left 测试判断该点是否都在所有边的左边或者右边,或者在某一条边上即可。
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const double eps = 1e-4;
struct Point { double x, y; };
vector<Point>P;
int to_left(Point a, Point b, Point c) {
Point X = { b.x - a.x,b.y - a.y }, Y = { c.x - a.x,c.y - a.y };
double ans = X.x * Y.y - X.y * Y.x ;
double res = X.x * Y.x + Y.y * X.y;
if (abs(ans) < eps) {
return 0;
}
else if (ans > 0)return 1;
return -1;
}
int main() {
for (int i = 0; i < 3; i++) {
Point p; cin >> p.x >> p.y;
P.push_back(p);
}
P.push_back(P[0]);
int T; cin >> T;
while (T--) {
Point p; cin >> p.x >> p.y;
int f1 = 0, f2 = 0, f = 0;
for (int i = 0; i < 3; i++) {
int cnt = to_left(P[i], P[i + 1], p);
if (cnt == 1)f1++;
else if (cnt == -1)f2++;
else {
if (p.x >= min(P[i].x, P[i + 1].x) && p.x <= max(P[i].x, P[i + 1].x) && p.y >= min(P[i].y, P[i + 1].y) && p.y <= max(P[i].y, P[i + 1].y)) {
f = 1;
}
}
}
if (f1 == 3 || f2 == 3 || f == 1)cout << "YES\n";
else cout << "NO\n";
}
return 0;
}
极角排序
A.Sort Points by Argument
题意
对每个点的极角进行排序
板子题
题解
简单解释一下板子
我们将平面分为五个部分,分别为 x 轴以下,原点,x 轴正半轴,x 轴以上,x 轴以上。
对于某一部分通过叉积来判断大小。
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
//constexpr double eps = 1e-8;
constexpr long long eps = 0;
constexpr double PI = 3.1415926535897932384l;
template<typename T> struct point
{
T x, y;
bool operator==(const point& a) const { return (abs(x - a.x) <= eps && abs(y - a.y) <= eps); }
bool operator<(const point& a) const { if (abs(x - a.x) <= eps) return y < a.y - eps; return x < a.x - eps; }
point operator+(const point& a) const { return { x + a.x,y + a.y }; }
point operator-(const point& a) const { return { x - a.x,y - a.y }; }
point operator-() const { return { -x,-y }; }
point operator*(const T k) const { return { k * x,k * y }; }
point operator/(const T k) const { return { x / k,y / k }; }
T operator*(const point& a) const { return x * a.x + y * a.y; } //点乘 Dot
T operator^(const point& a) const { return x * a.y - y * a.x; } //叉乘 Cross
// 1:左 -1:右 0:线上
//auto的原理就是根据后面的值,来自己推测前面的类型是什么。
//return *this返回当前对象, return this返回当前对象的地址(指向当前对象的指针)
int toleft(const point& a) const { const auto t = (*this) ^ a; return (t > eps) - (t < -eps); }
T len2() const { return (*this) * (*this); }
T dis2(const point& a) const { return (a - (*this)).len2(); }
double len() const { return sqrt(len2()); }
double dis(const point& a) const { return sqrt(dis2(a)); }
//夹角
double ang(const point& a) const { return acos(max(-1.0, min(1.0, ((*this) * a) / (len() * a.len())))); }
//逆时针旋转rad
point rot(const double rad) const { return { x * cos(rad) - y * sin(rad),x * sin(rad) + y * cos(rad) }; }
};
using Point = point<ll>;
bool argcmp(const Point& a, const Point& b) {
auto quad = [](const Point& a) {
if (a.y < -eps)return 1;
if (a.y > eps)return 4;
if (a.x < -eps)return 5;
if (a.x > eps)return 3;
return 2;
};
int qa = quad(a), qb = quad(b);
if (qa != qb)return qa < qb;
auto t = a ^ b;
//极轴排序
//if (abs(t) <= eps)return a * a < b* b - eps;
return t > eps;
}
ll n, x, y;
vector<Point>vec;
int main() {
cin >> n;
for (int i = 0; i < n; i++) {
Point p; cin >> p.x >> p.y;
vec.push_back(p);
}
sort(vec.begin(), vec.end(), argcmp);
for (int i = 0; i < vec.size(); i++) {
cout << vec[i].x << " " << vec[i].y << "\n";
}
return 0;
}