Triangle Collision(二分 平面几何)

http://acm.hdu.edu.cn/contests/contest_showproblem.php?pid=1008&cid=881

题意:

给出一个正三角形和一个质点的位置矢量及速度矢量,点严格在三角形内部。点如果撞到边界会像光路一样镜面反射,速度不变。统计第k次碰撞的时间,保证前k次不会撞到夹角。

解析:

等同于在这个图中移动

Triangle Collision(二分 平面几何)_第1张图片
以水平的那种线为例。

直接二分运动的距离 l e n len len,求出终点在 y y y轴上的投影高度: h e i g h t height height

h e i g h t / ( 3 2 L ) height/(\dfrac{\sqrt3}{2}L) height/(23 L)就是对应的倍数 P P P

  • 如果 V y ≥ 0 Vy\geq0 Vy0,碰撞水平线的条数为 ⌊ P ⌋ \lfloor P\rfloor P(向上碰撞 P ∈ ( 0 , i n f ] P\in(0,inf] P(0,inf]
  • 否则为 1 − ⌈ P ⌉ 1-\lceil P\rceil 1P(向下碰撞 P ∈ [ − inf ⁡ , 1 ) P\in[-\inf,1) P[inf,1)

再来考虑碰撞左边的线 ( y = 3 x + b ) (y=\sqrt3x+b) (y=3 x+b)和右边的线 ( y = − 3 x + b ) (y=-\sqrt3x+b) (y=3 x+b)

最简单的处理方法就是把坐标系换一下后套用上面的办法求。

换坐标系:
Triangle Collision(二分 平面几何)_第2张图片

以左边的线(图3)为例,方向向量直接逆时针旋转120度即可,点的话 Y Y Y坐标为与左边的线的距离, X X X坐标为与法线的距离, X X X坐标需要用叉积判断一下正负。

代码:

/*
 *  Author : Jk_Chen
 *    Date : 2020-07-28-14.13.52
 */
#include
using namespace std;
#define LL long long
#define F double
#define rep(i,a,b) for(int i=(int)(a);i<=(int)(b);i++)
#define per(i,a,b) for(int i=(int)(a);i>=(int)(b);i--)
#define mmm(a,b) memset(a,b,sizeof(a))
#define pb push_back
#define pill pair
#define fi first
#define se second
void test(){cerr<<"\n";}
template<typename T,typename... Args>void test(T x,Args... args){cerr<<"> "<<x<<" ";test(args...);}
const LL mod=1e9+7;
const int maxn=1e5+9;
const int inf=0x3f3f3f3f;
LL rd(){ LL ans=0; char last=' ',ch=getchar();
    while(!(ch>='0' && ch<='9'))last=ch,ch=getchar();
    while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();
    if(last=='-')ans=-ans; return ans;
}
#define rd rd()
/*_________________________________________________________begin*/
F eps=1e-8;
const F pi=acos(-1);
struct point{
    F x,y;
    point(){}
    point(F x,F y):x(x),y(y){}
};
typedef point Vector;
typedef point Point;
Vector operator + (Vector a, Vector b){//向量加法
    return Vector(a.x + b.x, a.y + b.y);
}
Vector operator - (Vector a, Vector b){//向量减法
    return Vector(a.x - b.x, a.y - b.y);
}
Vector operator * (Vector a, double p){//向量数乘
    return Vector(a.x*p, a.y*p);
}
Vector operator / (Vector a, double p){//向量数除
    return Vector(a.x / p, a.y / p);
}
int dcmp(double x){//精度三态函数(>0,<0,=0)
    if (fabs(x) < eps)return 0;
    else if (x > 0)return 1;
    return -1;
}
bool operator == (const Point &a, const Point &b){//向量相等
    return dcmp(a.x - b.x) == 0 && dcmp(a.y - b.y) == 0;
}
double Dot(Vector a, Vector b){//内积
    return a.x*b.x + a.y*b.y;
}
double Length(Vector a){//模
    return sqrt(Dot(a, a));
}
double Angle(Vector a, Vector b){//夹角,弧度制
    return acos(Dot(a, b) / Length(a) / Length(b));
}
double Cross(Vector a, Vector b){//外积
    return a.x*b.y - a.y*b.x;
}
Vector Rotate(Vector a, double rad){//逆时针旋转
    return Vector(a.x*cos(rad) - a.y*sin(rad), a.x*sin(rad) + a.y*cos(rad));
}
double Distance(Point a, Point b){//两点间距离
    return sqrt((a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y - b.y));
}
double DistanceToLine(Point A, Point M, Point N){//点A到直线MN的距离,Error:MN=0
    return fabs(Cross(A - M, A - N) / Distance(M, N));
}


double L;
point P,V;
int k;

LL Find(Point P,Vector V,double H,double len,double len_V){/// 点、方向向量、三角形高度、运动长度、方向向量长度
    LL ans=0;
    double height=P.y+len*(V.y/len_V);
    height/=H;
    if(dcmp(V.y)>=0){
        ans+=(LL)floor(height);
    }
    else{
        ans+=1-(LL)ceil(height);
    }
    return ans;
}

int main(){
    int t=rd;
    while(t--){
        scanf("%lf%lf%lf%lf%lf",&L,&P.x,&P.y,&V.x,&V.y);
        k=rd;

        double len_V=Length(V);
        double H=L/2*sqrt(3);

        Point PL,PR,VL=Rotate(V,pi*2/3),VR=Rotate(V,-pi*2/3); /// 相对坐标系下的坐标

        Point PLmid=Point(-L/4,H/2),PRmid=Point(L/4,H/2);

        PL.y=DistanceToLine(P,Point(-L/2,0),Point(0,H));
        PL.x=DistanceToLine(P,Point(L/2,0),PLmid);
        if(Cross(P-PLmid,Point(L/2,0)-PLmid)<0)
            PL.x*=-1;

        PR.y=DistanceToLine(P,Point(L/2,0),Point(0,H));
        PR.x=DistanceToLine(P,Point(-L/2,0),PRmid);
        if(Cross(P-PRmid,Point(-L/2,0)-PRmid)<0)
            PR.x*=-1;

        double l=0,r=1e11;
        double Ans;
        while((r-l)/(max(1.0,l))>eps){
            double len=(l+r)/2;
            //test(len);

            LL ans=0;
            ans+=Find(P,V,H,len,len_V);
            ans+=Find(PL,VL,H,len,len_V);
            ans+=Find(PR,VR,H,len,len_V);

            if(ans>=k){
                //printf("G %lld\n",ans);
                Ans=len;
                r=len;
            }
            else{
                //printf("L %lld\n",ans);
                l=len;
            }
        }
        printf("%.10f\n",Ans/len_V);
    }
    return 0;
}

/*_________________________________________________________end*/

你可能感兴趣的:(平面几何)