【BZOJ5328】【SDOI2018】物理实验

【题目链接】

【思路要点】

  • 旋转、平移坐标系,使得直线导轨为X轴。
  • 注意到所有线段互不相交,且与X轴没有交点。
  • 对一二和三四象限的线段分别做一遍扫描线,求出每一段在X轴可见的线段到X轴的投影点以及其真实长度和投影长度的比值,并将一二和三四象限的结果合并。
  • 我们放置激光发射器的位置是无限多的,但投影点的个数是有限多的,并且我们发现一定有至少一个最优解满足激光发射器的某一个端点恰好为一个投影点。
  • Two Pointers或前缀和+二分即可解决。
  • 时间复杂度\(O(TNLogN)\)。

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 40005;
const double eps = 1e-6;
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; };
struct segment {point a, b; };
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}; }
double operator * (point a, point b) {return a.x * b.y - a.y * b.x; }
point operator * (point a, double b) {return (point) {a.x * b, a.y * b}; }
point operator / (point a, double b) {return (point) {a.x / b, a.y / b}; }
double moo(point a) {return sqrt(a.x * a.x + a.y * a.y); }
double dot(point a, point b) {return a.x * b.x + a.y * b.y; }
point unit(point a) {return a / moo(a); }
double gety (segment a, double x) {
	point tmp = a.b - a.a;
	tmp = tmp / (a.b.x - a.a.x) * (x - a.a.x);
	return (a.a + tmp).y;
}
struct info {int val; };
struct changes {double x; int home; bool type; };
segment a[MAXN], goal;
point axisx, axisy;
changes c[MAXN];
int length, tot; double nowx;
int upsum; double upx[MAXN], uprate[MAXN];
int downsum; double downx[MAXN], downrate[MAXN];
int cnt; double x[MAXN], rate[MAXN], sum[MAXN];
set <info> st;
bool cmptype;
bool operator < (info x, info y) {
	if (cmptype) return gety(a[x.val], nowx) < gety(a[y.val], nowx);
	return gety(a[x.val], nowx) > gety(a[y.val], nowx);
}
bool operator < (changes a, changes b) {
	return a.x < b.x;
}
point vary(point a) {
	point ans;
	a = a - goal.a;
	ans.x = dot(axisx, a);
	ans.y = dot(axisy, a);
	return ans;
}
int main() {
	int T; read(T);
	while (T--) {
		int n; read(n);
		for (int i = 1; i <= n; i++) {
			read(a[i].a.x), read(a[i].a.y);
			read(a[i].b.x), read(a[i].b.y);
		}
		read(goal.a.x), read(goal.a.y);
		read(goal.b.x), read(goal.b.y);
		read(length);
		axisx = unit(goal.b - goal.a);
		axisy = (point) {-axisx.y, axisx.x};
		for (int i = 1; i <= n; i++) {
			a[i].a = vary(a[i].a);
			a[i].b = vary(a[i].b);
			if (a[i].a.x > a[i].b.x) swap(a[i].a, a[i].b);
		}
		//getup
		tot = 0;
		for (int i = 1; i <= n; i++) {
			if (a[i].a.y > 0) {
				c[++tot] = (changes) {a[i].a.x, i, true};
				c[++tot] = (changes) {a[i].b.x, i, false};
			}
		}
		sort(c + 1, c + tot + 1);
		cmptype = true;
		st.clear();
		upsum = tot;
		for (int i = 1; i <= tot; i++) {
			nowx = c[i].x;
			upx[i] = c[i].x;
			if (c[i].type) st.insert((info) {c[i].home});
			else st.erase((info) {c[i].home});
			if (st.empty()) uprate[i] = 0;
			else {
				int tmp = (*st.begin()).val;
				point tnp = a[tmp].b - a[tmp].a;
				tnp = tnp / tnp.x; uprate[i] = moo(tnp);
			}
		}
		//getup
		//getdown
		tot = 0;
		for (int i = 1; i <= n; i++) {
			if (a[i].a.y < 0) {
				c[++tot] = (changes) {a[i].a.x, i, true};
				c[++tot] = (changes) {a[i].b.x, i, false};
			}
		}
		sort(c + 1, c + tot + 1);
		cmptype = false;
		st.clear();
		downsum = tot;
		for (int i = 1; i <= tot; i++) {
			nowx = c[i].x;
			downx[i] = c[i].x;
			if (c[i].type) st.insert((info) {c[i].home});
			else st.erase((info) {c[i].home});
			if (st.empty()) downrate[i] = 0;
			else {
				int tmp = (*st.begin()).val;
				point tnp = a[tmp].b - a[tmp].a;
				tnp = tnp / tnp.x; downrate[i] = moo(tnp);
			}
		}
		//getdown
		int upnow = 1, downnow = 1;
		double upr = 0, downr = 0; cnt = 0;
		while (upnow <= upsum || downnow <= downsum) {
			if (upnow > upsum) x[++cnt] = downx[downnow];
			else if (downnow > downsum) x[++cnt] = upx[upnow];
			else if (upx[upnow] < downx[downnow]) x[++cnt] = upx[upnow];
			else x[++cnt] = downx[downnow];
			while (upnow <= upsum && abs(x[cnt] - upx[upnow]) < eps) upr = uprate[upnow++];
			while (downnow <= downsum && abs(x[cnt] - downx[downnow]) < eps) downr = downrate[downnow++];
			rate[cnt] = upr + downr;
		}
		for (int i = 2; i <= cnt; i++)
			sum[i] = sum[i - 1] + rate[i - 1] * (x[i] - x[i - 1]);
		double ans = 0;
		for (int i = 1; i <= cnt - 1; i++) {
			double now = 0, tx = x[i] + length;
			if (tx >= x[cnt]) now = sum[cnt] - sum[i];
			else {
				int l = i, r = cnt;
				while (l < r) {
					int mid = (l + r + 1) / 2;
					if (tx >= x[mid]) l = mid;
					else r = mid - 1;
				}
				now = sum[r] - sum[i] + (tx - x[r]) * rate[r];
			}
			chkmax(ans, now);
		}
		for (int i = cnt; i >= 2; i--) {
			double now = 0, tx = x[i] - length;
			if (tx <= x[1]) now = sum[i];
			else {
				int l = 1, r = i;
				while (l < r) {
					int mid = (l + r) / 2;
					if (tx <= x[mid]) r = mid;
					else l = mid + 1;
				}
				now = sum[i] - sum[r] + (x[r] - tx) * rate[r - 1];
			}
			chkmax(ans, now);
		}
		printf("%.10lf\n", ans);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_39972971/article/details/80522707