2024 ICPC 昆明 M 题 题解
· 4 min read ·
—
简要题意
凸包包含一个圆,选择凸包的两个顶点切一刀,使得不经过 圆且不包含圆的那部分面积尽可能大。
题解
枚举切割线的一个端点A,另一个端点离它越远越好。 因此使用旋转卡壳,在枚举A的同时维护逆时针最远的B,使得AB位于圆心固定的一侧,且圆心到AB 的距离大于等于半径。 顺时针最远的C同理。 复杂度O(n)。
参考代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using ull = unsigned long long;
using db = double;
#define dbg(x) cerr << #x << " = " << (x) << endl;
#define vdbg(a) cout << #a << " = "; for (auto x : a) cout << x << " "; cout << endl;
#define int long long
struct Point {ll x, y; };
using Vec = Point;
struct Circle { Point O; ll r; };
struct Seg { Point A, B; };
Vec operator-(const Vec &u, const Vec &v) {
return {u.x - v.x, u.y - v.y};
}
ll cross(const Vec &u, const Vec &v) {
return u.x * v.y - u.y * v.x;
}
ll len2(const Vec &v) {
return v.x * v.x + v.y * v.y;
}
ll area2(const vector<Point> &p) {
int n = p.size();
ll ans = 0;
for (int i = 0; i < n; ++i) {
int j = (i + 1) % n;
ans += cross(p[i], p[j]);
}
return abs(ans);
}
void solve() {
int n; cin >> n;
ll x, y, r; cin >> x >> y >> r;
Point o = {x, y};
Circle O = {o, r};
vector<Point> g(n);
for (int i = 0; i < n; ++i) {
ll x, y; cin >> x >> y;
g[i] = {x, y};
g.push_back(g[i]);
}
ll ans = 0;
// int j = 1;
int j = 1;
ll s = 0;
for (int i = 0; i < n; ++i) {
while (1) {
int nxt = j + 1;
ll cro = cross(g[nxt] - g[i],o - g[i]);
if ((__int128_t)cro * cro < (__int128_t)r * r * len2(g[i] - g[nxt]) || cro <= 0) break;
cro = cross(g[j] - g[i], g[nxt] - g[i]);
s += abs(cro);
j = nxt;
}
ans = max(ans, s);
ll cro = cross(g[j] - g[i], g[i + 1] - g[i]);
s -= abs(cro);
}
cout << ans << "\n";
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout << fixed << setprecision(12);
int T = 1;
cin >> T;
while (T--) solve();
return 0;
}