[ZJOI2007]粒子运动 题解

[ZJOI2007]粒子运动 题解

题目地址:洛谷:【P4724】[ZJOI2007]粒子运动 – 洛谷、BZOJ:Problem 1094. — [ZJOI2007]粒子运动

题目描述

阿Q博士正在观察一个圆形器皿中的粒子运动。不妨建立一个平面直角坐标系,圆形器皿的圆心坐标为(x0, y0
),半径为R。器皿中有若干个粒子,假设第i个粒子在时刻0的位置为(xi, yi),速度为(vxi,vyi)(注:这是一个速度向量,若没有发生碰撞,t时刻的位置应该是(xi + t * vxi, yi + t * vyi) )。假设所有粒子的运动互不干扰;若某个粒子在某个时刻碰到了器皿壁,将发生完全弹性碰撞,即速度方向按照碰撞点的切线镜面反射,且速度大小不变(如图)。认为碰撞是瞬间完成的。
particle
尽管碰撞不会影响粒子的速率,但是粒子却会受到一定的伤害,所以若某一个粒子碰撞了k次器皿壁,那么在第k次碰撞时它便会消亡。
出于研究的需要,阿Q博士希望知道从时刻0到所有粒子都消亡这段时间内,所有粒子之间的最近距离是什么。你能帮助他么?

题意简述

圆形容器中有$n$个粒子,各给定初始位置和速度,粒子会与容器壁碰撞,碰撞时发生反射,如图,速度会改变方向但不改变大小。每个粒子最多能和容器壁碰撞$k$次,否则会消失。求所有时刻所有粒子两两距离的最小值。

输入输出格式

输入格式:
第一行包含三个实数,分别为x0, y0, R,即圆形器皿的圆心坐标及半径。第二行包含两个正整数N, k,分别表示粒子的总数与消亡碰撞次数。接下来N行每行四个实数,分别为xi, yi, vxi , vyi,保证(xi, yi)都在圆内且(vxi, vyi)非零。

输出格式:
仅包含一个实数,即所有粒子的历史最近距离,精确到小数点后三位。

输入输出样例

输入样例#1:

0 0 10
2 10
0 -5 0 1
5 0 1 0

输出样例#1:

7.071

说明

对于所有的数据,2 ≤N ≤100。1≤k ≤100。
请注意实数精度问题。

题解

没用到计算几何算法的计算几何题。
我们考虑,在一段连续时间内,研究两个粒子的距离,假如在这段时间内不发生碰撞,则距离可以表示为一个二次函数,假设第一个粒子的位置可以表示为$P+tV$($P$是起始位置,$V$是速度向量),第二个可以表示为$P’+tV’$,则可以列出距离的表达式$f(t) = \mathrm{length}[(P+tV) – (P’+tV’)]$,展开后得到一个二次函数。既然是二次函数,我们可以在$O(1)$时间内求得区间最小值。
因此,本题的大体思路是,枚举每两个粒子,在每一个两个粒子都不发生碰撞的连续区间内求区间最小距离,来更新最后的答案。这个复杂度是$O(n^2k)$的。
我用了对称的方法解决反射的速度变化,先把射线的起点对称至碰撞点处的半径另一侧,然后在另一侧构造新的速度向量,并处理长度即可。过程如图所示。
sol

代码

// Code by KSkun, 2018/7
#include <cstdio>
#include <cctype>
#include <cmath>

#include <algorithm>

const int MAXN = 105;
const double EPS = 1e-8, PI = acos(-1);

inline int dcmp(double x) {
    if(fabs(x) < EPS) return 0; else return x < 0 ? -1 : 1;
}

struct Vector {
    double x, y;
    Vector(double x = 0, double y = 0) : x(x), y(y) {}
    inline Vector operator+(Vector rhs) {
        return Vector(x + rhs.x, y + rhs.y);
    }
    inline Vector operator-(Vector rhs) {
        return Vector(x - rhs.x, y - rhs.y);
    }
    inline Vector operator*(double rhs) {
        return Vector(x * rhs, y * rhs);
    }
    inline Vector operator/(double rhs) {
        return Vector(x / rhs, y / rhs);
    }
};

typedef Vector Point;

inline double dot(Vector u, Vector v) {
    return u.x * v.x + u.y * v.y;
}

inline double cross(Vector u, Vector v) {
    return u.x * v.y - u.y * v.x;
}

inline double length(Vector v) {
    return sqrt(dot(v, v));
}

inline double angle(Vector u, Vector v) {
    return acos(dot(u, v) / length(u) / length(v));
}

inline Vector rotate(Vector v, double rad) {
    return Vector(v.x * cos(rad) - v.y * sin(rad), v.x * sin(rad) + v.y * cos(rad));
}

struct Line {
    Point p; Vector v;
    Line(Point p = Point(), Vector v = Vector()) : p(p), v(v) {}
};

struct Ray : public Line {
    Ray(Point p = Point(), Vector v = Vector()) : Line(p, v) {}
};

struct Circle {
    Point c; double r;
    Circle(Point c = Point(), double r = 0) : c(c), r(r) {}
};

inline Point intersection(Circle o, Ray l) {
    double x0 = o.c.x, y0 = o.c.y, a = l.p.x, b = l.p.y, c = l.v.x, d = l.v.y;
    double A = c * c + d * d, B = 2 * (a * c - c * x0 + b * d - d * y0), 
        C = a * a - 2 * a * x0 + x0 * x0 + b * b - 2 * b * y0 + y0 * y0 - o.r * o.r;
    double delta = B * B - 4 * A * C;
    double t1 = (-B + sqrt(delta)) / 2 / A, t2 = (-B - sqrt(delta)) / 2 / A;
    if(dcmp(t1) > 0) return l.p + l.v * t1; else return l.p + l.v * t2;
}

inline Point projection(Line l, Point p) {
    return l.p + l.v * (dot(l.v, p - l.p) / dot(l.v, l.v));
}

inline double distance(Point p1, Point p2) {
    return length(p1 - p2);
}

inline double getmin(double A, double B, double C, double l, double r) {
    double v1 = A * l * l + B * l + C, v2 = A * r * r + B * r + C, v3 = 1e10;
    double m = -B / 2 / A;
    if(m >= l && m <= r) v3 = A * m * m + B * m + C;
    return std::min(std::min(v1, v2), v3);
}

Circle o;
int n, k;
Point p[MAXN];
Vector v[MAXN];

inline double sqr(double x) {
    return x * x;
}

inline double getmin(int a, int b) {
    int ak = 0, bk = 0; double res = 1e10;
    Ray ra(p[a], v[a]), rb(p[b], v[b]);
    while(ak < k && bk < k) {
        Point ai = intersection(o, ra), bi = intersection(o, rb);
        double t1 = distance(ra.p, ai) / length(ra.v), t2 = distance(rb.p, bi) / length(rb.v);
        double nt = std::min(t1, t2);
        double A = sqr(ra.v.y - rb.v.y) + sqr(ra.v.x - rb.v.x),
            B = 2 * ((ra.p.y - rb.p.y) * (ra.v.y - rb.v.y) 
                + (ra.p.x - rb.p.x) * (ra.v.x - rb.v.x)),
            C = sqr(ra.p.y - rb.p.y) + sqr(ra.p.x - rb.p.x);
        res = std::min(res, getmin(A, B, C, 0, nt));
        if(dcmp(nt - t1) == 0) {
            ak++; 
            Point proj = projection(Line(o.c, o.c - ai), ra.p);
            Point p1(proj.x * 2 - ra.p.x, proj.y * 2 - ra.p.y);
            Vector v1 = p1 - ai; v1 = v1 / length(v1) * length(ra.v);
            ra.p = ai; ra.v = v1;
        } else {
            ra.p = ra.p + ra.v * nt;
        }
        if(dcmp(nt - t2) == 0) {
            bk++; 
            Point proj = projection(Line(o.c, o.c - bi), rb.p);
            Point p1(proj.x * 2 - rb.p.x, proj.y * 2 - rb.p.y);
            Vector v1 = p1 - bi; v1 = v1 / length(v1) * length(rb.v);
            rb.p = bi; rb.v = v1;
        } else {
            rb.p = rb.p + rb.v * nt;
        }
    }
    return res;
}

int main() {
    double x0, y0, r;
    scanf("%lf%lf%lf", &x0, &y0, &r);
    o = Circle(Point(x0, y0), r);
    scanf("%d%d", &n, &k);
    for(int i = 1; i <= n; i++) {
        double xi, yi, vxi, vyi;
        scanf("%lf%lf%lf%lf", &xi, &yi, &vxi, &vyi);
        p[i] = Point(xi, yi); v[i] = Vector(vxi, vyi);
    }
    double ans = 1e10;
    for(int i = 1; i <= n; i++) {
        for(int j = i + 1; j <= n; j++) {
            ans = std::min(ans, getmin(i, j));
        }
    }
    printf("%.3lf", dcmp(ans) > 0 ? sqrt(ans) : 0);
    return 0;
}


发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据