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