【CodeChef】Imagine Polygons

本文介绍了一种计算两个三维多边形在三维空间中交叠体积的方法,通过投影和积分来解决复杂几何问题,适用于计算机图形学和物理模拟等领域。

【题目链接】

【思路要点】

  • 首先显然的一点是当且仅当两个多边形在 x x x 轴上的跨度不一样,输出 − 1 -1 1
  • 考虑一个 x x x 轴上的坐标 i i i ,在满足 x = i x=i x=i 的平面上的任意一点 ( i , y , z ) (i,y,z) (i,y,z) 能够被计入答案当且仅当 ( i , y ) (i,y) (i,y) x − y x-y xy 轴的投影多边形内, ( i , z ) (i,z) (i,z) x − z x-z xz 轴的投影多边形内。
  • 令直线 x = i x=i x=i x − y x-y xy 轴的投影多边形的公共部分长度为 f ( i ) f(i) f(i) ,与 x − z x-z xz 轴的投影多边形的公共部分长度为 g ( i ) g(i) g(i) ,则答案为 ∫ M i n x M a x x f ( x ) ∗ g ( x ) ∗ d x \int_{Minx}^{Maxx}f(x)*g(x)*dx MinxMaxxf(x)g(x)dx
  • x x x 轴按照出现过的 x x x 坐标分段,每一段的 f ( x ) , g ( x ) f(x),g(x) f(x),g(x) 均为一次函数,可以方便地求解定积分。
  • 时间复杂度 O ( ( N + M ) L o g ( N + M ) ) O((N+M)Log(N+M)) O((N+M)Log(N+M))

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
struct point {double x, y; };
point operator + (point a, point b) {return (point) {a.x + b.x, a.y + b.y}; }
point operator - (point a, point b) {return (point) {a.x - b.x, a.y - b.y}; }
point a[MAXN], b[MAXN], sa[MAXN], sb[MAXN];
int n, m, tot; double p[MAXN];
set <double> st; map <double, int> home;
double Int(double l, double r, double a, double b, double c) {
	double ans = (r - l) * c;
	ans += (r * r / 2 - l * l / 2) * b;
	ans += (r * r * r / 3 - l * l * l / 3) * a;
	return ans;
}
int main() {
	int T; read(T);
	while (T--) {
		tot = 0;
		st.clear();
		home.clear();
		read(n);
		double Mina = 1e10, Maxa = -1e10;
		for (int i = 1; i <= n; i++) {
			read(a[i].x), read(a[i].y);
			st.insert(a[i].x);
			chkmin(Mina, a[i].x);
			chkmax(Maxa, a[i].x);
			sa[i] = (point) {0, 0};
		}
		a[n + 1] = a[1];
		read(m);
		double Minb = 1e10, Maxb = -1e10;
		for (int i = 1; i <= m; i++) {
			read(b[i].x), read(b[i].y);
			st.insert(b[i].x);
			chkmin(Minb, b[i].x);
			chkmax(Maxb, b[i].x);
			sb[i] = (point) {0, 0};
		}
		if (Minb != Mina || Maxb != Maxa) {
			puts("-1");
			continue;
		}
		b[m + 1] = b[1];
		for (auto x : st) {
			p[++tot] = x;
			home[x] = tot;
		}
		for (int i = 1; i <= n; i++) {
			int l = home[a[i].x], r = home[a[i + 1].x];
			double k = (a[i + 1].y - a[i].y) / (a[i + 1].x - a[i].x);
			if (l < r) sa[l] = sa[l] - (point) {k, a[i].y - k * a[i].x}, sa[r] = sa[r] + (point) {k, a[i].y - k * a[i].x};
			else if (l > r) sa[l] = sa[l] - (point) {k, a[i + 1].y - k * a[i + 1].x}, sa[r] = sa[r] + (point) {k, a[i + 1].y - k * a[i + 1].x};
		}
		for (int i = 1; i <= m; i++) {
			int l = home[b[i].x], r = home[b[i + 1].x];
			double k = (b[i + 1].y - b[i].y) / (b[i + 1].x - b[i].x);
			if (l < r) sb[l] = sb[l] - (point) {k, b[i].y - k * b[i].x}, sb[r] = sb[r] + (point) {k, b[i].y - k * b[i].x};
			else if (l > r) sb[l] = sb[l] - (point) {k, b[i + 1].y - k * b[i + 1].x}, sb[r] = sb[r] + (point) {k, b[i + 1].y - k * b[i + 1].x};
		}
		double ans = 0;
		for (int i = 1; i <= tot - 1; i++) {
			sa[i] = sa[i - 1] + sa[i];
			sb[i] = sb[i - 1] + sb[i];
			ans += Int(p[i], p[i + 1], sa[i].x * sb[i].x, sa[i].x * sb[i].y + sa[i].y * sb[i].x, sa[i].y * sb[i].y);
		}
		printf("%.10lf\n", ans);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值