【YBT2023寒假Day14 A】切割蛋糕(计算几何)

切割蛋糕

题目链接:YBT2023寒假Day14 A

题目大意

给你一个圆,圆心在原点,每次有一条直线,切掉圆中不包含原点的部分。
(直线给出的部分是它在于圆两个交点形成的线段的垂直平分线跟 x 正半轴的角度和这条直线跟垂直平分线交点到源点的距离)
然后最后问你剩下的那个图形圆弧部分的长度和与直线部分的长度和。

思路

不难想到可以直接上半平面交然后随便做(圆拆成很多条直线就行)
但我半平面写挂了,用一个别的好写写法。

首先你可以算出每条直线它与圆的两个交点,分别在圆上的角度。
那我们可以算出没有被切去的圆弧的长度了。

然后至于算直线,我可以枚举每一个直线,看它被保留了多少。
(一开始是它跟圆两个交点的距离)
那看保留我们就枚举别的直线,找到交点分别属于这个直线的两边的,然后算两个直线的交点,更改保留的部分。
不难想到保留的部分一定是一个区间,因为每次保留(或者删去)的部分都是一个你直线的一个前缀或者后缀。
那把每个直线保留的长度加上就是直线的答案了。

代码

#include<cmath>
#include<cstdio>
#include<algorithm>

using namespace std;

const int N = 55;
const double Pi = acos(-1.0);
struct dian {
	double x, y;
}a[N], b[N];
int n, m, l[N], r[N];
double RR, L[N], R[N], p[N], val[N];
bool in[N], no[N];

dian operator +(dian x, dian y) {
	return (dian){x.x + y.x, x.y + y.y};
}

dian operator -(dian x, dian y) {
	return (dian){x.x - y.x, x.y - y.y};
}

dian operator *(dian x, double y) {
	return (dian){x.x * y, x.y * y};
}

double operator *(dian x, dian y) {
	return x.x * y.x + x.y * y.y;
}

double operator ^(dian x, dian y) {
	return x.x * y.y - x.y * y.x;
}

struct line {
	dian x, y;
};

dian Meet(line a, line b) {
	double k = ((b.y - b.x) ^ (a.x - b.x)) / ((a.y - a.x) ^ (b.y - b.x));
	return a.x + (a.y - a.x) * k;
}

dian Meet(dian ax, dian ay, dian bx, dian by) {
	double k = ((by - bx) ^ (ax - bx)) / ((ay - ax) ^ (by - bx));
	return ax + (ay - ax) * k;
}

double dis(dian x, dian y) {
	return sqrt((x.x - y.x) * (x.x - y.x) + (x.y - y.y) * (x.y - y.y));
}

int main() {
	freopen("cake.in", "r", stdin);
	freopen("cake.out", "w", stdout);
	
	scanf("%d %lf", &n, &RR);
	for (int i = 1; i <= n; i++) {
		double A, h; scanf("%lf %lf", &A, &h);
		A = A / 180 * Pi; double B = acos(h / RR);
		L[i] = A - B; R[i] = A + B;
		while (L[i] < 0) L[i] += 2 * Pi;
		while (R[i] >= 2 * Pi) R[i] -= 2 * Pi;
		p[++m] = L[i]; p[++m] = R[i];
		a[i] = (dian){cos(L[i]), sin(L[i])};
		b[i] = (dian){cos(R[i]), sin(R[i])};
	}
	
	sort(p + 1, p + m + 1); m = unique(p + 1, p + m + 1) - p - 1;
	double ans1 = 0, ans2 = 2 * Pi;
	for (int i = 1; i <= n; i++) {
		l[i] = lower_bound(p + 1, p + m + 1, L[i]) - p;//离散化
		r[i] = lower_bound(p + 1, p + m + 1, R[i]) - p;
		for (int j = l[i]; j != r[i]; j = j % m + 1) {
			if (in[j]) continue; in[j] = 1;
			double x = p[j % m + 1] - p[j];
			while (x < 0) x += 2 * Pi;
			ans2 -= x;
		}
	}
	for (int i = 1; i <= m; i++) in[i] = 0;
	for (int i = 1; i <= n; i++) {
		for (int j = l[i]; j != r[i]; j = j % m + 1) in[j] = 1;
		double ls = 0, rs = dis(a[i], b[i]);
		for (int j = 1; j <= n; j++) {
			if (i == j) continue;
			if (!in[l[j]] && !in[r[j]]) continue;
			if (in[l[j]] && in[r[j]]) {//这条直线的作用被完全代替
				no[j] = 1; continue;
			}
			double awa = dis(Meet(a[i], b[i], a[j], b[j]), a[i]);
			if (in[l[j]]) rs = min(rs, awa);
				else ls = max(ls, awa);
		}
		if (ls <= rs) val[i] = rs - ls;
		for (int j = l[i]; j != r[i]; j = j % m + 1) in[j] = 0;
	}
	
	for (int i = 1; i <= n; i++) {
		if (no[i]) continue;
		ans1 += val[i];
	}
	
	printf("%.10lf %.10lf", ans1 * RR, ans2 * RR);
	
	return 0;
}
posted @ 2023-02-24 10:38  あおいSakura  阅读(38)  评论(0编辑  收藏  举报