[BZOJ4920][Lydsy六月月赛]薄饼切割

[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;
}

原文地址:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/8509447.html

时间: 2024-08-30 01:16:43

[BZOJ4920][Lydsy六月月赛]薄饼切割的相关文章

【BZOJ4919】[Lydsy六月月赛]大根堆 线段树合并

[BZOJ4919][Lydsy六月月赛]大根堆 Description 给定一棵n个节点的有根树,编号依次为1到n,其中1号点为根节点.每个点有一个权值v_i. 你需要将这棵树转化成一个大根堆.确切地说,你需要选择尽可能多的节点,满足大根堆的性质:对于任意两个点i,j,如果i在树上是j的祖先,那么v_i>v_j. 请计算可选的最多的点数,注意这些点不必形成这棵树的一个连通子树. Input 第一行包含一个正整数n(1<=n<=200000),表示节点的个数. 接下来n行,每行两个整数v

bzoj 4921: [Lydsy六月月赛]互质序列

4921: [Lydsy六月月赛]互质序列 Time Limit: 1 Sec  Memory Limit: 256 MBSubmit: 188  Solved: 110[Submit][Status][Discuss] Description 你知道什么是"互质序列"吗?那就是所有数的最大公约数恰好为1的序列. "互质序列"非常容易找到,但是我们可以尝试通过删除这个序列的一个非空连续子序列来扩大它的最大公约数. 现在给定一个长度为n的序列,你需要从中删除一个非空连

【bzoj4922】[Lydsy六月月赛]Karp-de-Chant Number 贪心+背包dp

题目描述 给出 $n$ 个括号序列,从中选出任意个并将它们按照任意顺序连接起来,求以这种方式得到匹配括号序列的最大长度. 输入 第一行包含一个正整数n(1<=n<=300),表示括号序列的个数. 接下来n行,每行一个长度在[1,300]之间的括号序列,仅由小括号构成. 输出 输出一行一个整数,即最大长度,注意你可以一个序列也不选,此时长度为0. 样例输入 3 ()) ((() )() 样例输出 10 题解 贪心+背包dp 首先对于一个括号序列,有用的只有:长度.消耗'('的数目.以及'('减去

bzoj 4919: [Lydsy六月月赛]大根堆

Description 给定一棵n个节点的有根树,编号依次为1到n,其中1号点为根节点.每个点有一个权值v_i. 你需要将这棵树转化成一个大根堆.确切地说,你需要选择尽可能多的节点,满足大根堆的性质:对于任意两个点i,j,如果i在树上是j的祖先,那么v_i>v_j. 请计算可选的最多的点数,注意这些点不必形成这棵树的一个连通子树. Solution 思路比较直接 设 \(f[i][j]\) 表示\(i\)为子树的节点中,堆中最大值小于\(j\)的情况下能选的最多点数 转移时就是用一个前缀最大值更

【BZOJ4919】[Lydsy六月月赛]大根堆

题解: 首先裸的dp很好想 f[i][j]表示在i点,最大值<=j的点数最大值 看了别人的题解知道了可以用线段树合并来优化这个东西.. 我们考虑对于每个点,首先我们要合并它的子树 其实就是对于相同位置的点相加即可 然后考虑当前节点,我们应用f[v[x]-1]+1去更新v[x]-n之间的值(也就是取max操作) 然后取max这个东西是可以标记永久化的 原文地址:https://www.cnblogs.com/yinwuxiao/p/9060320.html

【BZOJ4953】lydsy七月月赛 F DP

[BZOJ4953]lydsy七月月赛 F 题面 题解:设f[i][j]表示第i个强度取为j时的最小误差.那么每次转移时,我们只计算j'和j之间的像素点带来的误差,于是有: $f[i][j]=min(f[i-1][k]+g(k..mid,k)+g(mid+1...j,j))|mid={k+j\over 2}$ 其中,&g(a,b)=P_a*(a-b)^2\\=P_a*a*a-2*P_a*a*b+2*P_a*b*b& 于是维护P_a*a*a,P_a*a,P_a的前缀和即可. #include

【BZOJ4950】lydsy七月月赛 C 二分图最大匹配

[BZOJ4950]lydsy七月月赛 C 题面 题解:比较直接的想法就是:每行,每列的最大值都留下,剩下的格子都变成1.但是如果一个格子既是行的最大值又是列的最大值,那么我们只需要把它留下即可.这就变成了一个二分图最大匹配问题,乱搞即可. #include <cstdio> #include <cstring> #include <iostream> using namespace std; typedef long long ll; int n,m,cnt,now;

【BZOJ4952】lydsy七月月赛 E 二分答案

[BZOJ4952]lydsy七月月赛 E 题面 题解:傻题...二分答案即可,精度有坑. #include <cstdio> #include <cstring> #include <iostream> using namespace std; double eps=1e-9; int n,t; int d[1010],s[1010]; bool solve(double x) { double ret=0; for(int i=1;i<=n;i++) ret+

[BZOJ 4832][lydsy 4月赛] 抵制克苏恩

题面贴一发 [Lydsy2017年4月月赛]抵制克苏恩 Time Limit: 1 Sec  Memory Limit: 128 MBSubmit: 443  Solved: 164[Submit][Status][Discuss] Description 小Q同学现在沉迷炉石传说不能自拔.他发现一张名为克苏恩的牌很不公平.如果你不玩炉石传说,不必担心,小Q 同学会告诉你所有相关的细节.炉石传说是这样的一个游戏,每个玩家拥有一个 30 点血量的英雄,并且可以用牌 召唤至多 7 个随从帮助玩家攻击