【SDOI2018】物理实验

题目

考虑让直线导轨成为\(x\)轴,钦定\((x_1,y_1)\)成为坐标原点,其余点的坐标都减去\((x_1,y_1)\)。之后需要使得\((x_2,y_2)\)落在\(x\)轴上,求一下\((x_2,y_2)\)\(x\)轴的夹角\(\alpha\),让所有点都顺时针旋转\(\alpha\)就好了。

点旋转后的坐标变换是老经典问题了,\((x,y)\)顺时针旋转\(\alpha\)之后就变成了\((x\cos\alpha+y\sin\alpha,-x\sin\alpha+y\cos \alpha)\)

考虑一段挡板在\(x\)轴的投影是\([l,r]\),这段挡板到\(x\)轴的夹角为\(\beta\);如果激光发射器与投影的交长度为\(l\),那么就会在挡板被激光照射到的长度就是\(\frac{l}{\cos\beta}\)

这表明了我们一定能将所有挡板都转化成\((l,r,v)\)的形式,即这段挡板在\(x\)轴上的投影为\([l,r]\),极角余弦值的倒数为\(v\);这样对答案的影响就是,\(v\)乘上投影与激光发射器的交的长度

考虑处理出所有的\((l,r,v)\),由于挡板不能被激光穿过,所以只保留距离\(x\)轴最近的挡板;对于一条线段\((x_1,y_1,x_2,y_2)\),视为在\(x_1\)处插入它,在\(x_2\)处将其删除;用\(set\)来维护当前距离\(x\)轴最近的线段。由于题目中保证了任意两条线段不会相互接触,于是在两个相邻的端点之间最靠近\(x\)轴的线段只有一条。直接访问\(set\)的begin迭代器即可。

显然我们可以在\(O(1)\)的时间内判断两条线段谁更更靠近\(x\)轴,于是可以直接给\(set\)写一个比较函数,这样就非常方便。

最后将问题转化成了找到一个长度为\(L\)的线段,使得这条线段与投影的带权交最大。

不难发现将线段的端点卡在投影的端点处会取到最优的答案,于是我们将所有投影排好序,正反两边双指针即可。

于是这样就做完了,时间复杂度\(O(Tn\log n)\)

然而实际上非常难写,我们求投影的时候要分在\(x\)轴上方和下方两种情况分别求,分别求完之后还需要合并,双指针的时候也得进行一番讨论。

据说十分卡精度,还需要long double,但是直接用double就莽过去了也是非常刺激。

代码

#include<bits/stdc++.h>
#define db double
#define re register
#define LL long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
inline int read() {
	char c=getchar();int r=1,x=0;while(c<'0'||c>'9'){if(c=='-')r=-1;c=getchar();};
	while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+c-48,c=getchar();return x*r;
}
const db Pi=acos(-1),eps=1e-15;
const int maxn=5e4+5;
struct Pt{db x,y;}S,E;
struct Line{Pt s,t;}L[maxn];
struct wkL{db p,v;int o,rk;}a[maxn<<1];
struct Seg{db l,r,v;}seg[2][maxn<<1],b[maxn<<4];
struct Point{db p,v;int o;}d[maxn<<2];
int n,Len,tot,sz[2],cnt,num;db val[maxn];
inline int dcmp(db A,db B) {return A+eps>B&&A-eps<B;}
inline int cmp(const wkL &A,const wkL &B) {return A.p<B.p;}
inline int ctp(const Point &A,const Point &B) {return A.p<B.p;}
inline db operator*(const Pt &A,const Pt &B) {return A.x*B.y-A.y*B.x;}
inline Pt operator+(const Pt &A,const Pt &B) {return (Pt){A.x+B.x,A.y+B.y};}
inline Pt operator-(const Pt &A,const Pt &B) {return (Pt){A.x-B.x,A.y-B.y};}
inline Pt rotate(const Pt &A,db det) {
	db s=sin(det),c=cos(det);
	return (Pt){A.x*c+A.y*s,A.y*c-A.x*s};
}
struct cop {
	bool operator() (int A,int B) {
		if(L[A].s.x<L[B].s.x) {
			db t=L[B].s.x-L[A].s.x;
			t*=(L[A].t.y-L[A].s.y);
			t/=(L[A].t.x-L[A].s.x);
			t+=L[A].s.y;
			return t<L[B].s.y;
		}
		else {
			db t=L[A].s.x-L[B].s.x;
			t*=(L[B].t.y-L[B].s.y);
			t/=(L[B].t.x-L[B].s.x);
			t+=L[B].s.y;
			return L[A].s.y<t;
		}
	}
};
std::set<int,cop> s;
inline void calc(int op) {
	std::sort(a+1,a+tot+1,cmp);
	for(re int i=1;i<=tot;++i) 	
		if(a[i].o==1) val[a[i].rk]=a[i].v;
	db nw=-1e18;s.clear();
	for(re int i=1;i<=tot;++i) {
		if(!s.empty()&&!dcmp(nw,a[i].p)) {
			seg[op][++sz[op]].l=nw;
			seg[op][sz[op]].r=a[i].p;
			seg[op][sz[op]].v=val[*(s.begin())];
		}
		nw=a[i].p;
		if(a[i].o==-1) s.erase(a[i].rk);
		if(a[i].o==1) s.insert(a[i].rk);
	}
}
inline void Main_solve() {
	n=read();
	for(re int i=1;i<=n;i++) 
		L[i].s.x=read(),L[i].s.y=read(),L[i].t.x=read(),L[i].t.y=read();
	S.x=read(),S.y=read(),E.x=read(),E.y=read(),Len=read();
	for(re int i=1;i<=n;i++)L[i].s=L[i].s-S,L[i].t=L[i].t-S;E=E-S;
	db Det=atan2(E.y,E.x);
	for(re int i=1;i<=n;i++)L[i].s=rotate(L[i].s,Det);
	for(re int i=1;i<=n;i++)L[i].t=rotate(L[i].t,Det);
	for(re int i=1;i<=n;i++) 
		if(L[i].s.x>L[i].t.x) std::swap(L[i].s,L[i].t);
	tot=0;sz[0]=sz[1]=0;
	for(re int i=1;i<=n;i++) 
		if(L[i].s.y>0) {
			db det=atan2((L[i].t-L[i].s).y,(L[i].t-L[i].s).x);
			det=1.0/cos(det);
			a[++tot].p=L[i].s.x;
			a[tot].o=1;
			a[tot].v=det;
			a[tot].rk=i;
			a[++tot].p=L[i].t.x;
			a[tot].o=-1;
			a[tot].rk=i;
		}
	calc(0);tot=0;
	for(re int i=1;i<=n;i++) 
		if(L[i].s.y<0) {
			L[i].s.y=fabs(L[i].s.y);
			L[i].t.y=fabs(L[i].t.y);
			db det=atan2((L[i].t-L[i].s).y,(L[i].t-L[i].s).x);
			det=1.0/cos(det);
			a[++tot].p=L[i].s.x;
			a[tot].o=1;
			a[tot].v=det;
			a[tot].rk=i;
			a[++tot].p=L[i].t.x;
			a[tot].o=-1;
			a[tot].rk=i;
		}  
	cnt=0,num=0;
	calc(1);
	for(re int i=1;i<=sz[0];i++) {
		d[++cnt].p=seg[0][i].l;
		d[cnt].o=1;d[cnt].v=seg[0][i].v;
		d[++cnt].p=seg[0][i].r;
		d[cnt].o=-1;d[cnt].v=-seg[0][i].v;
	}
	for(re int i=1;i<=sz[1];++i) {
		d[++cnt].p=seg[1][i].l;
		d[cnt].o=1;d[cnt].v=seg[1][i].v;
		d[++cnt].p=seg[1][i].r;
		d[cnt].o=-1;d[cnt].v=-seg[1][i].v;
	}
	std::sort(d+1,d+cnt+1,ctp);
	db nw=-1e18,sv=0;int nsz=0;
	for(re int i=1;i<=cnt;++i) {
		if(nsz&&!dcmp(nw,d[i].p)) {
			b[++num].l=nw;
			b[num].r=d[i].p;
			b[num].v=sv;
		}
		nsz+=d[i].o;sv+=d[i].v;nw=d[i].p;
	}
	int lp=1;db tmp=0,ans=0;
	for(re int i=1;i<=num;++i) {
		if(lp>=i) tmp-=(b[i-1].r-b[i-1].l)*b[i-1].v;
		while(lp<=num&&(b[i].l+Len>b[lp].r||dcmp(b[i].l+Len,b[lp].r))) {
			if(lp>=i) tmp+=(b[lp].r-b[lp].l)*b[lp].v;
			++lp;
		}
		if(lp<=num) {
			db t=(b[i].l+Len)-b[lp].l;
			if(t<0)t=0;t*=b[lp].v;
			ans=max(ans,t+tmp);
		}else ans=max(ans,tmp);
	}
	for(re int i=1;i<=num;i++)std::swap(b[i].l,b[i].r),b[i].l=-b[i].l,b[i].r=-b[i].r;
	std::reverse(b+1,b+num+1);
	lp=1,tmp=0;
	for(re int i=1;i<=num;++i) {
		if(lp>=i) tmp-=(b[i-1].r-b[i-1].l)*b[i-1].v;
		while(lp<=num&&(b[i].l+Len>b[lp].r||dcmp(b[i].l+Len,b[lp].r))) {
			if(lp>=i) tmp+=(b[lp].r-b[lp].l)*b[lp].v;
			++lp;
		}
		if(lp<=num) {
			db t=(b[i].l+Len)-b[lp].l;
			if(t<0)t=0;t*=b[lp].v;
			ans=max(ans,t+tmp);
		}else ans=max(ans,tmp);
	}
	printf("%.15lf\n",ans);
		 
}
int main() {
	for(re int T=read();T;--T)Main_solve();
	return 0;
}
posted @ 2020-05-29 14:37  asuldb  阅读(180)  评论(0编辑  收藏  举报