xht37 的洛谷博客

xht37's blog:https://www.xht37.com/

题解 CF704E 【Iron Man】

CF704E Iron Man

题意

  • 给定一棵 $n$ 个点的树。
  • 有 $m$ 个人,第 $i$ 个人会在 $t_i$ 时刻出现在 $v_i$,并以每时刻 $c_i$ 条边的速度向 $u_i$ 移动,到达 $u_i$ 立刻消失。出现的时段是左闭右闭的,因此如果 $v_i = u_i$,则在 $t_i$ 时刻 $i$ 仍会出现。
  • 求第一次有人相遇的时刻,或判断始终无人相遇。
  • $n,m \le 10^5$, $t_i,c_i \le 10^4$。

题解

首先考虑一条链的情况。

暴力做法显然是对 $m$ 个人两两求答案然后取 $\min$,时间复杂度 $\mathcal O(m^2)$ 无法接受。

考虑用 multiset 按时间顺序维护所有人的相对位置,则只有在 multiset 中相邻的两个人有可能是答案。

时间复杂度 $\mathcal O(m \log m)$。

扩展到树上,将树进行树链剖分,则每个人被划分为 $\mathcal O(\log n)$ 条重链和轻链。

对每条链的答案求 $\min$ 即为最终答案,时间复杂度 $\mathcal O(n + m \log m \log n)$。

代码

struct frac {
    ll x, y;
    inline frac() {}
    inline frac(ll _x, ll _y = 1ll) : x(_x), y(_y) {
        ll g = __gcd(abs(x), abs(y));
        if (y < 0) g = -g;
        x /= g, y /= g;
    }
    inline friend frac operator + (frac e, frac b) { return frac(e.x * b.y + e.y * b.x, e.y * b.y); }
    inline friend frac operator - (frac e, frac b) { return frac(e.x * b.y - e.y * b.x, e.y * b.y); }
    inline friend frac operator * (frac e, frac b) { return frac(e.x * b.x, e.y * b.y); }
    inline friend frac operator / (frac e, frac b) { return frac(e.x * b.y, e.y * b.x); }
    inline friend bool operator < (frac e, frac b) { return e.x * b.y < b.x * e.y; }
    inline friend bool operator > (frac e, frac b) { return e.x * b.y > b.x * e.y; }
    inline friend bool operator <= (frac e, frac b) { return e.x * b.y <= b.x * e.y; }
    inline friend bool operator >= (frac e, frac b) { return e.x * b.y >= b.x * e.y; }
    inline friend bool operator == (frac e, frac b) { return e.x * b.y == b.x * e.y; }
};

const int N = 1e5 + 7, inf = 1e9;
int n, m, d[N], f[N], s[N], son[N], dfn[N], top[N], num;
vi e[N];
double Ans = inf;
frac tim, ans;
struct P {
    frac k, b, l, r;
    inline P() {}
    inline P(frac k, frac b, frac l, frac r) : k(k), b(b), l(l), r(r) {}
    inline friend bool operator < (P a, P b) {
        if (a.k * tim + a.b == b.k * tim + b.b)
            return a.l == b.l ? a.r == b.r ? a.k < b.k : a.r < b.r : a.l < b.l;
        return a.k * tim + a.b < b.k * tim + b.b;
    }
};
multiset<P> st;
vector<P> heavy[N], light[N];

inline int lca(int x, int y) {
    while (top[x] != top[y]) {
        if (d[top[x]] < d[top[y]]) swap(x, y);
        x = f[top[x]];
    }
    return d[x] < d[y] ? x : y;
}

inline frac dist(int x, int y) {
    return frac(d[x] + d[y] - 2 * d[lca(x,y)]);
}

void dfs(int x) {
    s[x] = 1;
    for (int y : e[x])
        if (y != f[x]) {
            f[y] = x, d[y] = d[x] + 1, dfs(y), s[x] += s[y];
            if (s[y] > s[son[x]]) son[x] = y;
        }
}

void dfs(int x, int p) {
    dfn[x] = ++num, top[x] = p;
    if (son[x]) dfs(son[x], p);
    for (int y : e[x])
        if (y != son[x] && y != f[x]) dfs(y, y);
}

inline void solve(int x, int y, frac s, frac v) {
    frac t = s + dist(x, y) / v;
    while (top[x] != top[y])
        if (d[top[x]] > d[top[y]]) {
            int p = top[x];
            heavy[p].pb(P(frac(0) - v, frac(d[x]) + v * s, s, s + frac(d[x] - d[p]) / v));
            s = s + frac(d[x] - d[p]) / v, x = top[x], p = f[x];
            light[x].pb(P(frac(0) - v, frac(d[x]) + v * s, s, s + frac(d[x] - d[p]) / v));
            s = s + frac(d[x] - d[p]) / v, x = f[x];
        } else {
            int p = top[y];
            heavy[p].pb(P(v, frac(d[y]) - v * t, t - frac(d[y] - d[p]) / v, t));
            t = t - frac(d[y] - d[p]) / v, y = top[y], p = f[y];
            light[y].pb(P(v, frac(d[y]) - v * t, t - frac(d[y] - d[p]) / v, t));
            t = t - frac(d[y] - d[p]) / v, y = f[y];
        }
    int p = top[x];
    if (d[x] > d[y]) heavy[p].pb(P(frac(0) - v, frac(d[x]) + v * s, s, t));
    else heavy[p].pb(P(v, frac(d[y]) - v * t, s, t));
}

inline frac get(P a, P b) {
    if (a.k == b.k) return a.b == b.b ? max(a.l, b.l) : frac(inf);
    frac o = (b.b - a.b) / (a.k - b.k);
    return o >= max(a.l, b.l) && o <= min(a.r, b.r) ? o : frac(inf);
}

inline void work(vector<P> p) {
    st.clear(), ans = frac(inf);
    vector<pair<P, bool>> q;
    for (P x : p) q.pb(mp(x, 1)), q.pb(mp(x, 0)); 
    sort(q.begin(), q.end(), [](pair<P, bool> x, pair<P, bool> y) {
        frac tx = x.se ? x.fi.l : x.fi.r, ty = y.se ? y.fi.l : y.fi.r;
        return tx == ty ? x.se > y.se : tx < ty;
    });
    for (auto x : q) {
        frac now = x.se ? x.fi.l : x.fi.r;
        if (now >= ans) break;
        tim = now;
        if (x.se) {
            auto tmp = st.insert(x.fi), pre = tmp, suf = tmp;
            if (pre != st.begin()) ans = min(ans, get(*--pre, *tmp));
            if (++suf != st.end()) ans = min(ans, get(*tmp, *suf));
        } else {
            auto tmp = st.lower_bound(x.fi), pre = tmp, suf = tmp;
            if (++suf != st.end() && pre != st.begin()) ans = min(ans, get(*--pre, *suf));
            st.erase(tmp);
        }
    }
    Ans = min(Ans, 1.0 * ans.x / ans.y);
}

int main() {
    rd(n), rd(m);
    for (int i = 1, x, y; i < n; i++) rd(x), rd(y), e[x].pb(y), e[y].pb(x);
    d[1] = 1, dfs(1), dfs(1, 1);
    for (int i = 1, t, c, x, y; i <= m; i++)
        rd(t), rd(c), rd(x), rd(y), solve(x, y, frac(t), frac(c));
    for (int i = 1; i <= n; i++) work(heavy[i]), work(light[i]);
    if (Ans == inf) return prints("-1"), 0;
    printf("%.10f\n", Ans);
    return 0;
}

2020-03-16 18:46:29 in 题解