计算几何专题训练
- 牛客多校10 J Illuminations
点到凸包切点,转换为求环上最少区间进行覆盖
#include <bits/stdc++.h>
using namespace std;
const double eps = 1e-15;
inline static int dcmp(double x) { return x < -eps ? -1 : x > eps ? 1 : 0; }
struct xl {
double x, y;
int id;
xl() {}
void in() {
scanf("%lf%lf", &x, &y);
}
xl(double x, double y) : x(x), y(y) {}
xl operator+(const xl &rhs) const { return xl(x + rhs.x, y + rhs.y); }
xl operator-(const xl &rhs) const { return xl(x - rhs.x, y - rhs.y); }
xl operator*(const double &rhs) const { return xl(x * rhs, y * rhs); }
xl operator/(const double &rhs) const { return xl(x / rhs, y / rhs); }
double angle(){return atan2(y,x);}
};
double D_(const xl &a, const xl &b) {
return a.x * b.x + a.y * b.y;
}
double X_(const xl &a, const xl &b) {
return a.x * b.y - a.y * b.x;
}
double onleft(xl &a, xl &b, xl &c) {
return X_(b - a, c - a);
}
int n, m;
xl a[400020];
xl b[200020];
xl base;
int you(xl q) {
int ret = 0;
int lo = 1, hi = n - 1;
while (lo <= hi) {
bool dnl = onleft(q, a[lo + 1], a[lo]) > 0;
int mid = (lo + hi + 1) >> 1;
bool dnm = onleft(q, a[mid + 1], a[mid]) > 0;
if (dnm) {
if (onleft(q, a[ret], a[mid]) > 0)
ret = mid;
}
if (dnl) {
if (onleft(q, a[ret], a[lo]) > 0)
ret = lo;
if (dnm && onleft(q, a[lo], a[mid]) > 0)
hi = mid - 1;
else lo = mid + 1;
} else {
if (!dnm && onleft(q, a[lo], a[mid]) > 0)
lo = mid + 1;
else hi = mid - 1;
}
}
return ret == 0 ? n : ret;
}
int zuo(xl q)
{
int ret = 0;
int lo = 1, hi = n - 1;
while (lo <= hi) {
bool dnl = onleft(q, a[lo - 1], a[lo]) < 0;
int mid = (lo + hi + 1) >> 1;
bool dnm = onleft(q, a[mid - 1], a[mid]) < 0;
if (dnm) {
if (onleft(q, a[ret], a[mid]) < 0)
ret = mid;
}
if (dnl) {
if (onleft(q, a[ret], a[lo]) < 0)
ret = lo;
if (dnm && onleft(q, a[lo], a[mid]) < 0)
lo = mid + 1;
else hi = mid - 1;
} else {
if (!dnm && onleft(q, a[lo], a[mid]) < 0)
hi = mid - 1;
else lo = mid + 1;
}
}
return ret == 0 ? n : ret;
}
struct wocao {
int l, r;
int id;
wocao() {}
wocao(int ll, int rr, int iid) {
if (rr < ll)rr += n;
l = ll;
r = rr;
id = iid;
}
bool operator<(const wocao &rhs) const {
return l == rhs.l ? r > rhs.r : l < rhs.l;
}
};
wocao q[400020];
int nima[400040][22];
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++)a[i].in();
a[0] = a[n];
base = a[1];
for (int i = 1; i <= m; i++)
{
b[i].in(), b[i].id = i;
q[i]=wocao(you(b[i]),zuo(b[i]),i);
}
sort(q + 1, q + m + 1);
int Max = 0, ed = 0;
for (int i = 1; i <= m; i++) {
if (q[i].r <= Max)continue;
else {
Max = q[i].r;
q[++ed] = q[i];
}
}
m = ed;
for (int i = 1; i <= m; i++)q[i + m] = wocao(q[i].l + n, q[i].r + n, q[i].id);
for (int i = 1; i <= m; i++)
if (q[i + 1].l > q[i].r) {
puts("-1");
exit(0);
}
if(q[m].r-q[1].l<n)
{
puts("-1");
exit(0);
}
int now = 1;
for (int i = 1; i <= 2 * m; i++) {
while (q[now].l <= q[i].r && now <= 2 * m)now++;
nima[i][0] = now - 1;
}
for (int k = 1; k <= 19; k++) {
for (int i = 1; i <= 2 * m; i++) {
nima[i][k] = nima[nima[i][k - 1]][k - 1];
}
}
int ans = m + 1, o;
for (int i = 1; i <= m; i++) {
if(q[nima[i][18]].r-q[i].l<n)continue;
now=i;
int temp=0;
for(int j=18;j>=0;j--)
{
if(q[nima[now][j]].r-q[i].l<n)
now=nima[now][j],temp+=1<<j;
}
if(temp + 1<ans)ans=temp+1,o=i;
}
cout << ans + 1 << "\n";
now = o;
for (int i = 0; i <= ans; i++) {
printf("%d ", q[now].id);
now = nima[now][0];
}
return 0;
}
/*
7
2
0 0
2 1
3 2
4 4
3 6
2 7
0 8
9 4
-1 1
3 4
0 0
1 0
0 1
2 2
-1 3
-1 -1
-0.5 -0.5
*/
- gym101201 E Enclosure
求在原来凸包的基础上增加一个点的新凸包面积的最大值。
倍增预处理凸包上点\(i\)往后\(1<<k\)个点形成的多边形面积,求出其他点在凸包上的切点\(L,R\),用三角形面积减小多边形面积即是增加的面积。
#include <bits/stdc++.h>
using namespace std;
const double eps = 1e-15;
inline static int dcmp(double x) { return x < -eps ? -1 : x > eps ? 1 : 0; }
struct xl {
long long x{}, y{};
int id{};
xl() {}
void in() {
scanf("%lld%lld", &x, &y);
}
xl(long long x, long long y) : x(x), y(y) {}
xl operator+(const xl &rhs) const { return {x + rhs.x, y + rhs.y}; }
xl operator-(const xl &rhs) const { return {x - rhs.x, y - rhs.y}; }
bool operator<(const xl &rhs) const {
return x == rhs.x ? y < rhs.y : x < rhs.x;
}
};
long long D_(const xl &a, const xl &b) {
return a.x * b.x + a.y * b.y;
}
long long X_(const xl &a, const xl &b) {
return a.x * b.y - a.y * b.x;
}
long long onleft(xl &a, xl &b, xl &c) {
return X_(b - a, c - a);
}
int n, k, blen;
vector<xl> b, c;
int you(xl q) {
int ret = 0;
int lo = 1, hi = blen - 1;
while (lo <= hi) {
bool dnl = onleft(q, b[lo + 1], b[lo]) > 0;
int mid = (lo + hi + 1) >> 1;
bool dnm = onleft(q, b[mid + 1], b[mid]) > 0;
if (dnm) {
if (onleft(q, b[ret], b[mid]) > 0)
ret = mid;
}
if (dnl) {
if (onleft(q, b[ret], b[lo]) > 0)
ret = lo;
if (dnm && onleft(q, b[lo], b[mid]) > 0)
hi = mid - 1;
else lo = mid + 1;
} else {
if (!dnm && onleft(q, b[lo], b[mid]) > 0)
lo = mid + 1;
else hi = mid - 1;
}
}
return ret;
}
int zuo(xl q) {
int ret = 0;
int lo = 1, hi = blen - 1;
while (lo <= hi) {
bool dnl = onleft(q, b[lo - 1], b[lo]) < 0;
int mid = (lo + hi + 1) >> 1;
bool dnm = onleft(q, b[mid - 1], b[mid]) < 0;
if (dnm) {
if (onleft(q, b[ret], b[mid]) < 0)
ret = mid;
}
if (dnl) {
if (onleft(q, b[ret], b[lo]) < 0)
ret = lo;
if (dnm && onleft(q, b[lo], b[mid]) < 0)
lo = mid + 1;
else hi = mid - 1;
} else {
if (!dnm && onleft(q, b[lo], b[mid]) < 0)
hi = mid - 1;
else lo = mid + 1;
}
}
return ret;
}
xl a[100010];
void hull(vector<xl> &ret, int m) {
sort(a + 1, a + m + 1);
int rm = 0;
for (int i = 1; i <= m; i++) {
while (rm > 1 && dcmp(X_(ret[rm - 1] - ret[rm - 2], a[i] - ret[rm - 2])) < 0)ret.pop_back(), rm--;
ret.push_back(a[i]), rm++;
}
int kk = rm;
for (int i = m - 1; i >= 1; i--) {
while (rm > kk && dcmp(X_(ret[rm - 1] - ret[rm - 2], a[i] - ret[rm - 2])) < 0)ret.pop_back(), rm--;
ret.push_back(a[i]), rm++;
}
if (m > 1)rm--, ret.pop_back();
}
long long area[200010][20];//16
int main() {
cin >> n >> k;
for (int i = 1; i <= n; i++)a[i].in();
hull(b, k);
blen = b.size();
for (int i = 0; i < blen; i++)b.push_back(b[i]);
hull(c, n);
for (int i = 0; i < 2 * blen; i++)area[i][0] = 0;
for (int i = 1; i <= 16; i++) {
for (int j = 0; j < 2 * blen; j++)
if (j + (1 << i) < 2 * blen) {
int mid = j + (1 << (i - 1));
area[j][i] = area[j][i - 1] + area[mid][i - 1] +
abs(X_(b[mid] - b[j], b[mid] - b[j + (1 << i)]));
} else break;
}
long long ans = 0;
for (auto x:c) {
int l = you(x);
int r = zuo(x);
if (l == r)continue;
long long ar = abs(X_(x - b[l], x - b[r]));
if (r < l)r += blen;
int cha = r - l;
long long temp = 0;
for (int i = 16; i >= 0; i--)
if (cha & (1 << i)) {
int mid = l + (1 << i);
temp += abs(X_(b[l] - b[mid], b[mid] - b[r])) + area[l][i];
l = mid;
}
ans = max(ans, -temp + ar);
}
for (int i = 2; i < blen; i++)
ans += abs(X_(b[i] - b[i - 1], b[i] - b[0]));
// for (int i = 0; i < blen; i++)cerr << b[i].x << " " << b[i].y << "\n";
printf("%lld.%lld", ans / 2, ans % 2 ? 5 : 0);
return 0;
}
- codeforces250.D
对每个\(B\),其到原点的线段与左河岸交点为\((aa,\frac{a}{b}y')\),二分找到最近的\(A\)点
#include<bits/stdc++.h>
using namespace std;
int n, m, aa, bb, b[100010], l[100010];
double a[100010];
double ans = 10000000000000000000.;
int o1, o2;
double f(double x) { return x * x; }
double dis(double x1, double y1, double x2, double y2) {
return sqrt(f(x1 - x2) + f(y1 - y2));
}
void upd(int i, int j) {
double temp = dis(0, 0, aa, a[i]) + dis(aa, a[i], bb, b[j]) + l[j];
if (temp < ans) {
ans = temp;
o1 = i, o2 = j;
}
}
int main() {
scanf("%d%d%d%d", &n, &m, &aa, &bb);
for (int i = 1; i <= n; i++)scanf("%lf", &a[i]);
sort(a + 1, a + n + 1);
for (int i = 1; i <= m; i++)scanf("%d", &b[i]);
for (int i = 1; i <= m; i++)scanf("%d", &l[i]);
for (int i = 1; i <= m; i++) {
double x = 1. * b[i] / bb * aa;
int pos = lower_bound(a + 1, a + n + 1, x) - a;
if (pos == n + 1)upd(n, i);
else if (pos == 1)upd(1, i);
else upd(pos - 1, i), upd(pos, i);
}
cout << o1 << " " << o2;
return 0;
}