1 条题解

  • 0
    @ 2025-8-24 22:02:35

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar liaohaoping
    **

    搬运于2025-08-24 22:02:35,当前版本为作者最后更新于2018-08-14 15:13:00,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    这题数据范围并不大,显然不用什么复杂的优化,求出每个粒子每段(这里简称某粒子两次碰撞之间的部分为一段,包括时刻0到第一次碰撞)的信息就行了,通过观察可发现,只需要处理好以下三个问题(为了方便,我们把所有坐标分别减去圆心的坐标,再把圆心的坐标改为(0,0)):

    1、某粒子在某段与圆下次碰撞的位置

    2、某粒子在某次碰撞后vx,vy的变化

    3、粒子a的某一段与粒子b的某一段(保证某一时刻这两段同时存在)的历史最近距离

    先看问题1,若此段起始点为(stx,sty),速度为(vx,vy),设碰撞时间为t,则(stx+vx·t)²+(sty+vy·t)²=r² → stx²+vx²·t²+2·stx·vx·t+sty²+vy²·t²+2·sty·vy·t-r²=0 → (vx²+vy²)·t²+(2·stx·vx+2·sty·vy)·t+(stx²+sty²-r²)=0,我们设a=vx²+vy²,b=2·stx·vx+2·sty·vy,c=stx²+sty²-r²,则t=-b+sqrt(b²-4ac)/2a(舍去小解,小解一定不成立),再通过t算出位置即可。

    问题2也不难想,若与圆的碰撞点为(x,y),原速为(x1,x2),反弹后为(x2,y2),不难得出atan2(x2,y2)-atan2(x,y)=atan2(x,y)-atan2(x1,y1)(角相等),即atan2(x2,y2)=2·atan2(x,y)-atan2(x1,y1),设k=x2/y2,则k=tan(2·atan2(x,y)-atan2(x1,y1)),因为速度相同,所以x1²+y1²=x2²+y2²,设z=x1²+y1²,则y2²+k²·y2²=z → (k²+1)·y2²=z → y2=sqrt(x1²+y1²/k²+1),然后通过y2求出x2,需要注意的是,(-x2,-y2)在这里同样成立,我们可以通过带入一个点判断是否在圆内来判断取哪个解。

    问题3稍微难一点,为了方便,我们设tt=max(t1,t2)(t1,t2分别为两段开始的时刻),若(x1,y1),(x2,y2)分别为tt时刻两粒子的坐标,(vx1,vy1),(vx2,vy2)分别为两段的速度,y为两粒子间距离的平方,y=((x1+vx1·t)-(x2+vx2·t))²+((y1+vy1·t)-(y2+vy2·t))² → y=((x1-x2)+(vx1-vx2)·t)²+((y1-y2)+(vy1-vy2)·t)²我们设xx=x1-x2,yy=y1-y2,vx=vx1-vx2,vy=vy1-vy2,那么y=(xx+vx·t)²+(yy+vy·t)² → y=xx²+vx²·t²+2·xx·vx·t+yy²+vy²·t²+2·yy·vy·t → y=(vx²+vy²)·t²+(2·xx·vx+2·yy·vy)·t+(xx²+yy²),再设a=vx²+vy²,b=2·xx·vx+2·yy·vy,c=xx²+yy²,然后求出最小值y3=4ac-b²/4a即为历史最近距离,特殊情况特判一下就行了。

    我们一开始先用1、2点处理出每个粒子的每一段,再两个两个粒子求历史最近距离,找最小值即为答案。

    代码

    #include <cstdio>
    #include <cmath>
    #include <cstring>
    #include <cstdlib>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    inline int read()
    {
    	int x=0,w=0;char ch=getchar();
    	while(ch<'0'||ch>'9')w|=ch=='-',ch=getchar();
    	while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    	return w?-x:x;
    }
    struct line{double stx,sty,vx,vy,edt;};
    struct particle{line f[110];}p[110];
    int n,k;double r,z=0.000000001;
    inline void get_p(int i,int j,double stx,double sty,double vx,double vy)
    {
    	p[i].f[j].stx=stx,p[i].f[j].sty=sty,p[i].f[j].vx=vx,p[i].f[j].vy=vy;
    	double a=vx*vx+vy*vy,b=2*stx*vx+2*sty*vy,c=stx*stx+sty*sty-r*r;
    	double t=(-b+sqrt(b*b-4*a*c))/(2*a);
    	p[i].f[j].edt=p[i].f[j-1].edt+t;//求碰撞点 
    	if(j<k)
    	{
    		double edx=stx+vx*t,edy=sty+vy*t;
    		double kk=tan(atan2(edx,edy)*2-atan2(vx,vy));
    		double vy2=sqrt((vx*vx+vy*vy)/(kk*kk+1)),vx2=kk*vy2;
    		double tt=sqrt(r)/100000/((vx2+vy2)/2);tt=tt<0?-tt:tt;
    		double xx=(edx+vx2*tt),yy=(edy+vy2*tt);
    		if(xx*xx+yy*yy>=r*r)vx2=-vx2,vy2=-vy2;
    		get_p(i,j+1,edx,edy,vx2,vy2);//反弹 
    	}
    }
    inline double dis(double x1,double y1,double x2,double y2)
    {
    	if(x1==x2&&y1==y2)return 0;
    	double x=x1-x2,y=y1-y2;
    	return sqrt(x*x+y*y);
    }
    int main()
    {
    	register double xx,yy;register int i,j,ii,jj;
    	scanf("%lf%lf%lf",&xx,&yy,&r);n=read(),k=read();
    	for(i=1;i<=n;++i)
    	{
    		double x,y,vx,vy;scanf("%lf%lf%lf%lf",&x,&y,&vx,&vy);
    		x-=xx;y-=yy;p[i].f[0].edt=0;get_p(i,1,x,y,vx,vy);
    	}
    	double ans=1e50;
    	for(i=2;i<=n;++i)
    	{
    		for(j=1;j<i;++j)
    		{
    			jj=1;
    			for(ii=1;ii<=k;++ii)//第一个粒子处于第ii段 
    			{
    				--jj;
    				while(p[i].f[ii].edt>p[j].f[jj].edt&&jj<k)//判断为了使有某时刻两段(ii,jj+1)同时存在,ii是否需要++
    				{
    					++jj;
    					double t1=p[i].f[ii-1].edt,t2=p[j].f[jj-1].edt;
    					double x1=p[i].f[ii].stx,x2=p[j].f[jj].stx,y1=p[i].f[ii].sty,y2=p[j].f[jj].sty;
    					double vx1=p[i].f[ii].vx,vx2=p[j].f[jj].vx,vy1=p[i].f[ii].vy,vy2=p[j].f[jj].vy;
    					if(vx1==vx2&&vy1==vy2)//如果速度,方向相等,进行特殊处理 
    					{
    						double t=max(t1,t2),xx1=x1+vx1*(t-t1),xx2=x2+vx2*(t-t2),yy1=y1+vy1*(t-t1),yy2=y2+vy2*(t-t2);
    						ans=min(ans,dis(xx1,yy1,xx2,yy2));
    					}
    					else
    					{
    						double xx1=x1+vx1*(t1<t2?t2-t1:0),xx2=x2+vx2*(t1>t2?t1-t2:0),yy1=y1+vy1*(t1<t2?t2-t1:0),yy2=y2+vy2*(t1>t2?t1-t2:0);
    						double vx=vx1-vx2,vy=vy1-vy2;xx=xx1-xx2,yy=yy1-yy2;
    						double a=vx*vx+vy*vy,b=2*xx*vx+2*yy*vy,c=xx*xx+yy*yy;
    						double t=-(b/(2*a));
    						if(t<0)ans=min(ans,dis(xx1,yy1,xx2,yy2));//如果t<0,取最近的合法值(t=0) 
    						else if(t+max(t1,t2)>=p[i].f[ii].edt+z||t+max(t1,t2)>=p[j].f[jj].edt+z)//如果该时刻超出任意一段范围,取最近的合法值 
    						{
    							t=min(p[i].f[ii].edt,p[j].f[jj].edt),xx1=x1+vx1*(t-t1),xx2=x2+vx2*(t-t2),yy1=y1+vy1*(t-t1),yy2=y2+vy2*(t-t2);
    							ans=min(ans,dis(xx1,yy1,xx2,yy2));
    						}
    						else//否则正常计算 
    						{
    							double d=sqrt((4*a*c-b*b)/(4*a));
    							ans=min(ans,d);
    						}
    					}//求距离 
    				}
    			}
    		}
    	}
    	printf("%.3lf",ans);
    	return 0;
    }
    
    • 1

    信息

    ID
    3709
    时间
    1000ms
    内存
    125MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者