給定一個多邊形(島嶼)和一個圓(炸彈爆炸範圍),求兩者相交的面積。多邊形頂點數 n ≤ 100,000,圓心座標與半徑由物理公式計算得出。
以圓心為頂點,將多邊形劃分為 n 個三角形(第 i 個三角形由圓心、頂點 i、頂點 i+1 構成)。總相交面積即為所有三角形與圓相交面積之和(注意有向面積,需根據叉積符號決定正負)。
設三角形兩頂點為 A、B,圓心 O,半徑 r。根據 A、B 相對於圓的位置:
|cross(OA, OB)| / 2。AB 與圓的交點 T,分別計算三角形 OAT 或 OBT 面積與扇形面積。AB 與圓無交點或 1 個交點(相切):相交面積 = 扇形面積 r² * angle(A, B) / 2。AB 與圓有 2 個交點 P、Q:相交面積 = 大扇形面積 + 小三角形面積 - 小扇形面積 = r² * angle(A, B)/2 + |cross(OP, OQ)|/2 - r² * angle(P, Q)/2。// 向量運算
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); }
double dot(Vector A, Vector B) { return A.x*B.x + A.y*B.y; }
double length(Vector A) { return sqrt(dot(A, A)); }
double cross(Vector A, Vector B) { return A.x*B.y - A.y*B.x; }
double angle(Vector A, Vector B) { return acos(dot(A, B) / length(A) / length(B)); }
// 點到直線距離
double point_line_distance(Point P, Point A, Point B) {
Vector AP = P - A, AB = B - A;
return fabs(cross(AP, AB) / length(AB));
}
// 直線與圓交點(返回交點數 0/1/2,交點存入 v)
int circle_line_intersect(Circle C, Point A, Point B, vector<Point> &v) {
double dist = point_line_distance(C.c, A, B);
int d = dcmp(dist - C.r);
if (d > 0) return 0;
Point pro = point_line_projection(C.c, A, B);
if (d == 0) { v.push_back(pro); return 1; }
double len = sqrt(C.r*C.r - dist*dist);
Vector AB = B - A;
Vector l = AB / length(AB) * len;
v.push_back(pro + l);
v.push_back(pro - l);
return 2;
}
double circle_delta_intersect_area(Circle C, Point A, Point B) {
Vector CA = A - C.c, CB = B - C.c;
double da = length(CA), db = length(CB);
da = dcmp(da - C.r); db = dcmp(db - C.r);
if (da <= 0 && db <= 0) return fabs(cross(CA, CB)) * 0.5; // 兩點在圓內
vector<Point> v;
int num = circle_line_intersect(C, A, B, v);
double carea = C.r * C.r * PI;
if (da <= 0 && db > 0) { // A 內 B 外
Point t = point_on_segment(v[0], A, B) ? v[0] : v[1];
double area = fabs(cross(CA, t - C.c)) * 0.5;
double an = angle(CB, t - C.c);
return area + carea * an / PI / 2;
}
if (da > 0 && db <= 0) { // A 外 B 內
Point t = point_on_segment(v[0], A, B) ? v[0] : v[1];
double area = fabs(cross(CB, t - C.c)) * 0.5;
double an = angle(CA, t - C.c);
return area + carea * an / PI / 2;
}
// 兩點都在圓外
if (num == 2) {
double bigarea = carea * angle(CA, CB) / PI / 2;
double smallarea = carea * angle(v[0]-C.c, v[1]-C.c) / PI / 2;
double deltaarea = fabs(cross(v[0]-C.c, v[1]-C.c)) * 0.5;
return bigarea + deltaarea - smallarea;
}
return carea * angle(CA, CB) / PI / 2; // 相切或無交點
}
double circle_polygon_intersect_area() {
p[n] = p[0]; // 閉合
double ans = 0;
for (int i = 0; i < n; i++) {
double area = circle_delta_intersect_area(bomb, p[i], p[i+1]);
if (cross(p[i]-bomb.c, p[i+1]-bomb.c) < 0) area = -area; // 有向面積
ans += area;
}
return ans > 0 ? ans : -ans;
}
題目中炸彈落點由拋物線運動決定:t = sqrt(0.2 * h)(重力加速度取 10),水平位移 x1*t、y1*t,最終圓心為 (x + x1*t, y + y1*t)。