bzoj5328: [Sdoi2018]物理实验【坐标旋转+扫描线+set】

Description

小T这学期有物理实验课,为了顺利完成下一节课的实验,他打算在课前对实验内容进行预习。这次实验在一个二
维平面上进行,平面上放置了一条无限长的直线导轨,导轨上放置了一个长为L的激光发射器,激光发射器会向导
轨两侧沿导轨垂直方向同时发射宽度为L的平行激光束。平面上还放置了n个挡板,每个挡板可以看作是一条线段,
现在每个挡板都不和直线导轨接触,且和直线导轨的夹角不超过85度,任意两个挡板也不会相互接触,激光束不能
穿透这些挡板,并且会被挡板吸收掉,不会被挡板反射出去。
小T想确定一个激光发射器的位置使得被激光束照射到的挡板长度之和最大,你需要帮小T算出这个最大值

Input

第一行包含一个正整数T,表示测试数据的组数,对于每组测试数据,
第一行是一个整数n,表示挡板个数,
接下来n行,每行包含四个整数x1,y1,x2,y2,表示挡板的两端点分别是(x1,y1)和(x2,y2),保证(x1,y1)!=(x2,y2),
第n+2行是五个整数x1,y1,x2,y2,L,表示直线导轨经过了点(x1,y1)和(x2,y2),且激光发射器的长度为L,同样保证(x1,y1)!=(x2,y2)。
T <= 100,
1 <= n <= 10^4,
1 <= L <= 2*10^9,
所有坐标的绝对值不超过 10^9

Output

对于每组测试数据,输出一行,包含一个实数,表示激光束能照射到的挡板长度之和的最大值,要求相对误差不超过10^-6,
也就是说,令输出结果为a,标准答案为b,若满足|a-b|/max(1,b)<=10^-6,则输出结果会被认为是正确答案。

Sample Input

3

4

-3 2 -1 2

-1 -1 1 -1

0 1 2 1

2 -2 4 -2

0 0 1 0 2

4

1 1 3 3

2 1 4 2

3 1 5 1

3 -1 4 -1

0 0 -1 0 2

4

-2 0 1 2

1 3 -3 2

1 -3 5 -1

2 -1 4 3

0 0 1 1 2

Sample Output

3.000000000000000

3.118033988749895

4.251303782246768

解题思路:

先旋转坐标轴,记录线段的 s e c = 1 c o s 值,用set维护扫描线上离x轴最近的两条线段,最后再拿双指针扫一遍即可。

#include<bits/stdc++.h>
#define ll long long
#define LD long double
using namespace std;
int getint()
{
    int i=0,f=1;char c;
    for(c=getchar();(c!='-')&&(c<'0'||c>'9');c=getchar());
    if(c=='-')c=getchar(),f=-1;
    for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
    return i*f;
}
const int N=100005;
int n,m,q[N];
LD x[N][2],y[N][2],Sec[N],val[N],p[N],X0,Y0,X1,Y1,L,x0;
inline bool cmp(int a,int b){return ((a>0)?x[a][0]:x[-a][1])<((b>0)?x[b][0]:x[-b][1]);}
struct line
{
    int u;
    line(int _u=0):u(_u){}
    inline friend bool operator < (const line &a,const  line &b)
    {
        if(a.u==b.u)return false;
        LD ya=(y[a.u][0]-y[a.u][1])/(x[a.u][0]-x[a.u][1])*(x0-x[a.u][0])+y[a.u][0];
        LD yb=(y[b.u][0]-y[b.u][1])/(x[b.u][0]-x[b.u][1])*(x0-x[b.u][0])+y[b.u][0];
        return abs(ya)<abs(yb);
    }
};set<line>S_up,S_down;
void solve()
{
    n=getint(),m=0;
    for(int i=1;i<=n;i++)
    {
        x[i][0]=getint(),y[i][0]=getint(),x[i][1]=getint(),y[i][1]=getint();
        Sec[i]=sqrt((x[i][0]-x[i][1])*(x[i][0]-x[i][1])+(y[i][0]-y[i][1])*(y[i][0]-y[i][1]));
    }
    X0=getint(),Y0=getint(),X1=getint(),Y1=getint(),L=getint();
    if(X0>X1)swap(X0,X1),swap(Y0,Y1);
    LD dx=X1-X0,dy=Y1-Y0,len=sqrt(dx*dx+dy*dy),Sin=dy/len,Cos=dx/len;
    for(int i=1;i<=n;i++)
    {
        x[i][0]-=X0,x[i][1]-=X0,y[i][0]-=Y0,y[i][1]-=Y0;
        LD t1,t2,t3,t4;
        t1=x[i][0]*Cos+y[i][0]*Sin,t2=y[i][0]*Cos-x[i][0]*Sin;
        t3=x[i][1]*Cos+y[i][1]*Sin,t4=y[i][1]*Cos-x[i][1]*Sin;
        x[i][0]=t1,y[i][0]=t2,x[i][1]=t3,y[i][1]=t4;
    }
    for(int i=1;i<=n;i++)
    {
        if(x[i][0]>x[i][1])swap(x[i][0],x[i][1]),swap(y[i][0],y[i][1]);
        Sec[i]/=(x[i][1]-x[i][0]);
    }
    for(int i=1;i<=n;i++)q[++m]=i,q[++m]=-i;
    sort(q+1,q+m+1,cmp);
    for(int i=1;i<=m;i++)val[i]=0;
    for(int i=1,u;i<=m;i++)
    {
        if(q[i]>0)
        {
            u=q[i];x0=p[i]=x[u][0];
            if(!S_up.empty())val[i]+=Sec[S_up.begin()->u];
            if(!S_down.empty())val[i]+=Sec[S_down.begin()->u];
            ((y[u][0]>0)?S_up:S_down).insert(line(u));
        }
        else
        {
            u=-q[i];x0=p[i]=x[u][1];
            if(!S_up.empty())val[i]+=Sec[S_up.begin()->u];
            if(!S_down.empty())val[i]+=Sec[S_down.begin()->u];
            ((y[u][0]>0)?S_up:S_down).erase(line(u));
        }
    }
    LD res=0,ans=0,l=p[1]-L,r=p[1];int pl=1,pr=2;
    while(pr<=m)
    {
        LD dl=p[pl]-l,dr=p[pr]-r;
        if(dl>dr)res+=(val[pr]-val[pl])*dr,pr++,l+=dr,r+=dr;
        else if(dl<dr)res+=(val[pr]-val[pl])*dl,pl++,l+=dl,r+=dl;
        else res+=(val[pr]-val[pl])*dl,pl++,pr++,l+=dl,r+=dl;
        ans=max(ans,res);
    }
    printf("%.15Lf\n",ans);
}
int main()
{
    //freopen("laser.in","r",stdin);
    //freopen("laser.out","w",stdout);
    for(int T=getint();T;T--)solve();
    return 0;
}

猜你喜欢

转载自blog.csdn.net/cdsszjj/article/details/80455406