[BZOJ4920][Lydsy六月月赛]薄饼切割
试题描述
有一天,tangjz 送给了 quailty 一张薄饼,tangjz 将它放在了水平桌面上,从上面看下去,薄饼形成了一个 \(H \times W\) 的长方形。
tangjz 交给了 quailty 一根木棍,要求 quailty 将木棍轻轻放到桌面上。然后 tangjz 会以薄饼中心作为原点,将木棍绕着原点旋转一圈,将木棍扫过的部分切下来送给 quailty。
quailty 已经放好了木棍,请写一个程序帮助他们计算 quailty 得到了多少面积的薄饼。
输入
第一行包含一个正整数 \(T(1 \le T \le 1000)\),表示测试数据的组数。
每组数据包含一行 \(6\) 个整数 \(H,W,x_1,y_1,x_2,y_2(1 \le H,W \le 10000,|x_1|,|y_1|,|x_2|,|y_2| \le 10000)\),其中 \(H\) 和 \(W\) 表示薄饼的长和宽,\((x_1,y_1)\) 和 \((x_2,y_2)\) 分别表示木棍两端点的坐标。
输入数据保证木棍两端点不会重合。
输出
对于每组数据,输出一行一个实数,即 quailty 得到的面积,与标准答案的绝对或相对误差不超过 \(10^{-8}\) 时会被认为是正确的。
输入示例
2
3 2 -4 0 -4 -3
1 5 -4 -3 4 2
输出示例
0.0000000000000
4.4352192982310
数据规模及约定
见“输入”
题解
不难发现扫过的面就是一个大圆和长方形的交减去小圆和长方形的交。
下面令 \(P_1(x_1, y_1), P_2(x_2, y_2)\),\(O\) 为原点。
首先需要确定大圆小圆的半径。大圆的半径就是线段 \(P_1P_2\) 到原点的最大距离,即 \(\max \{ |OP_1|, |OP_2| \}\)。小圆的半径则是线段 \(P_1P_2\) 到原点的最小距离,如果过原点做 \(P_1P_2\) 所在直线的垂线与 \(P_1P_2\) 有交的话(这个可以用向量的点积判断),距离就是这个垂线段的长度,可以直接用面积法计算出来;如果没有交点那就是 \(\min \{ |OP_1|, |OP_2| \}\)。
接下来考虑如何计算一个半径为 \(r\),圆心为 \(O\) 的圆与四个顶点坐标分别为 \(\begin{Bmatrix} \left( -\frac{W}{2}, \frac{H}{2} \right), \left( \frac{W}{2}, \frac{H}{2} \right), \left( \frac{W}{2}, -\frac{H}{2} \right), \left( -\frac{W}{2}, -\frac{H}{2} \right) \end{Bmatrix}\) 的矩形的交集的面积。
这个东西还是分类讨论一下:
- 如果圆将整个矩形包含(即 \(2r \ge \sqrt{W^2 + H^2}\)),则面积为 \(W \cdot H\);
- 否则就是一个圆的面积减去几个弓形的面积(注意判断一下是否与上下、左右边界相交),这个弓形的面积就是扇形的面积减去一个三角形的面积,可以利用
acos(x)
(即 \(\arccos(x)\))函数得到扇形的角度从而得到扇形面积,用叉积计算三角形的面积。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <cmath>
using namespace std;
#define rep(i, s, t) for(int i = (s), mi = (t); i <= mi; i++)
#define dwn(i, s, t) for(int i = (s), mi = (t); i >= mi; i--)
int read() {
int x = 0, f = 1; char c = getchar();
while(!isdigit(c)){ if(c == ‘-‘) f = -1; c = getchar(); }
while(isdigit(c)){ x = x * 10 + c - ‘0‘; c = getchar(); }
return x * f;
}
const double pi = acos(-1.0);
struct Vec {
double x, y;
Vec() {}
Vec(double _, double __): x(_), y(__) {}
Vec operator - (const Vec& t) const { return Vec(x - t.x, y - t.y); }
double operator * (const Vec& t) const { return x * t.x + y * t.y; }
double operator ^ (const Vec& t) const { return x * t.y - y * t.x; }
double len() { return sqrt(x * x + y * y); }
};
double OutArea(double h, double a) {
double c = sqrt(a * a - h * h), S = pi * a * a * acos(h / a) / pi;
S -= abs(Vec(c, h) ^ Vec(-c, h)) / 2.0;
return S;
}
double Area(double H, double W, double r) {
if(H > W) swap(H, W);
if(r >= sqrt(H * H + W * W) / 2.0) return H * W;
double S = pi * r * r;
if(r > H / 2.0) S -= 2.0 * OutArea(H / 2.0, r);
if(r > W / 2.0) S -= 2.0 * OutArea(W / 2.0, r);
return S;
}
double calcDis(Vec p1, Vec p2) {
if(min((p1 - p2) * (Vec(0, 0) - p2), (p2 - p1) * (Vec(0, 0) - p1)) < 0) return min(p1.len(), p2.len());
return abs(p1 ^ p2) / (p1 - p2).len();
}
void work() {
int H = read(), W = read(), x1 = read(), y1 = read(), x2 = read(), y2 = read();
double l = calcDis(Vec(x1, y1), Vec(x2, y2)), r = max(Vec(x1, y1).len(), Vec(x2, y2).len());
printf("%.13lf\n", Area(H, W, r) - Area(H, W, l));
return ;
}
int main() {
int T = read();
while(T--) work();
return 0;
}