ZOJ 3763 —— Plasma Field(几何,数学)

题目:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3763

虽然题目有点长,但是仔细读下来发现也没什么。。。

给一个关于 t 的分段函数R(t):

When 0 ≤ t ≤ TR(t) = c*t

When T < t ≤ T*2, R(t) = c * (T * 2 - t).

然后对于每个点(xi,yi),有一个速度(vx,vy)

那么这个点在t时刻到点(x0,y0)的距离用d(t)表示。

题目就要求多少个点使得R(t)=d(t)在[0,2T]这个区间上有解。

由于d(t)直接表示带有根号,有点麻烦,所以我直接用d(t)和R(t)的平方来计算,下面用到的全部都是平方后的形式。

令F(t) = d(t)-R(t)

当0<=t<=T时,将已知的信息代入到F(t) 中可以得到(这个在纸上算下就行,我就不在这里展开了):

F(t) = A*t^2 + B*t +C

其中:A = vx*vx + vy*vy - c*c

B = 2*((x-x0)*vx + (y-y0)*vy)

C = (x-x0)^2 + (y-y0)^2

然后就是判断在[0,T]上是否有F(t)=0了,这个高中数学都学过了,找到区间的两个最值,看它们是否异号就行了。

至于最值,根据A如果是正的,说明可以求最小值,否则求最大值,剩下的最值在两个区间端点找就行了。

这里不用管在这个区间上二次函数长什么样,甚至它是不是二次都可以不用管,因为采用三分法计算最值的时候,单调的函数可以看作一种特例。

然后对于区间(T,2*T],方法跟上面类似,只是这个时候B要加上4*T*c*c,C要减去4*T*T*c*c,因为此时R(t)的公式不一样了。

PS:一开始很SB地在A是正的情况下求最大值,WA了很多次才发现这个已经不知道怎么形容的错误了。差点想开JAVA写高精了 = = 

#include<cstdio>
const double eps = 1e-8;
double x0, y0, c, T;
int n;
double x, y, vx, vy;
double A, B, C;
double F(double t){
    return A*t*t+B*t+C;
}
bool ok_min(double L, double R){
    double low=L, top=R, m1, m2;
    double s1, s2, s3;
    while(top-low>eps){
        m1 = low+(top-low)/3.0;
        m2 = top-(top-low)/3.0;
        s1 = F(m1);
        s2 = F(m2);
        if(s1<s2)   top = m2-eps;
        else    low = m1+eps;
    }
    s1 = F(L);
    s2 = F(R);
    s3 = F(low);
    if(s1*s3<eps || s2*s3<eps || s1*s2<eps)  return 1;
    else    return 0;
}
bool ok_max(double L, double R){
    double low=L, top=R, m1, m2;
    double s1, s2, s3;
    while(top-low>eps){
        m1 = low+(top-low)/3.0;
        m2 = top-(top-low)/3.0;
        s1 = F(m1);
        s2 = F(m2);
        if(s1<s2)   low = m1+eps;
        else    top = m2-eps;
    }
    s1 = F(L);
    s2 = F(R);
    s3 = F(low);
    if(s1*s3<eps || s2*s3<eps || s1*s2<eps)  return 1;
    else    return 0;
}
int check(){
    A = vx*vx+vy*vy-c*c;
    B = 2.0*((x-x0)*vx+(y-y0)*vy);
    C = (x-x0)*(x-x0)+(y-y0)*(y-y0);
    if(A<=0){
        if(ok_max(0,T))    return 1;
    }
    else{
        if(ok_min(0,T))    return 1;
    }
    B += 4.0*T*c*c;
    C -= 4.0*T*T*c*c;
    if(A<=0){
        if(ok_max(T,T*2))    return 1;
    }
    else{
        if(ok_min(T,T*2))    return 1;
    }
    return 0;
}
int main(){
    while(~scanf("%lf %lf %lf %lf", &x0, &y0, &c, &T)){
        scanf("%d", &n);
        int ans=0;
        while(n--){
            scanf("%lf %lf %lf %lf", &x, &y, &vx, &vy);
            ans += check();
        }
        printf("%d\n", ans);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值