Description
“鐵人”yyb 在玩遊戲。在一個 \(n\) 個點的樹上,yyb 放置了 \(m\) 個雞賊。每個雞賊有四個整數引數 \(t_i,c_i,v_i,u_i\),表示這個雞賊會在 \(t_i\) 時刻出現在點 \(v_i\),並以每時刻 \(c_i\) 條邊的速度向 \(u_i\) 點勻速移動,到達 \(u_i\) 點時立刻消失。
如果一個時刻有兩個雞賊在同一位置,它們會立刻爆炸。
(注意,如果一個雞賊的 \(u_i=v_i\) 那麼它會在 \(t_i\) 時刻出現,此時如果這個點有其它雞賊一樣會發生爆炸)
yyb 想知道最早有雞賊爆炸的時刻。如果自始至終都沒發生爆炸輸出 -1
。
如果你的答案和標準答案的絕對或相對誤差不超過 \(10^{-6}\) 那麼被視為正確。
Solution
注意到樹上問題很不好做,考慮透過樹剖將每組移動拆分成 \(O(\log n)\) 條鏈,然後轉化為對於所有的 \(O(n)\) 條鏈判斷。
那麼可以對於每個鏈用 \(x\) 表示時間,\(y\) 表示位置,所以每組移動就可以轉化為一個線段,然後需要求出這些線段的交點中 \(x\) 座標的最小值。
考慮掃描線,把每個線段左右端點的 \(x\) 座標作為關鍵點,容易發現對於一對不相交的線段一定滿足他們在所有的關鍵點時刻的 \(y\) 座標大小關係不變。
於是可以用 set 維護當前時刻每個線段的 \(y\) 座標,觀察之後會發現每對相交的線段一定滿足在某個相交之前的時刻他們在 set 上是相鄰的,所以只要每次加入一個線段時求出這個線段和它前驅、後繼的答案,如果答案已經小於當前時刻就說明找到了最終答案。
注意這裡 set 需要過載運算子,由於找到答案後會直接退出所以不會出現奇怪的問題。
時間複雜度:\(O(n+m\log^2n)\)。
Code
手寫分數類實現
#include <bits/stdc++.h>
#define int int64_t
using f64 = long double;
const int kMaxN = 1e5 + 5;
const f64 kEps = 1e-7;
struct Frac {
int x, y; // x / y
void fix() {
if (y < 0) x = -x, y = -y;
}
Frac(__int128_t _x = 0, __int128_t _y = 1) {
__int128_t d = std::__gcd(_x, _y);
x = _x / d, y = _y / d;
if (y < 0) x = -x, y = -y;
}
friend Frac operator +(Frac a, Frac b) { return Frac(a.x * b.y + a.y * b.x, a.y * b.y); }
friend Frac operator -(Frac a, Frac b) { return Frac(a.x * b.y - a.y * b.x, a.y * b.y); }
friend Frac operator *(Frac a, Frac b) { return Frac(a.x * b.x, a.y * b.y); }
friend Frac operator /(Frac a, Frac b) { return Frac(a.x * b.y, a.y * b.x); }
friend bool operator <(Frac a, Frac b) { a.fix(), b.fix(); return (__int128_t)a.x * b.y < (__int128_t)a.y * b.x; }
friend bool operator <=(Frac a, Frac b) { a.fix(), b.fix(); return (__int128_t)a.x * b.y <= (__int128_t)a.y * b.x; }
friend bool operator >(Frac a, Frac b) { a.fix(), b.fix(); return (__int128_t)a.x * b.y > (__int128_t)a.y * b.x; }
friend bool operator >=(Frac a, Frac b) { a.fix(), b.fix(); return (__int128_t)a.x * b.y >= (__int128_t)a.y * b.x; }
friend bool operator ==(Frac a, Frac b) { a.fix(), b.fix(); return (__int128_t)a.x * b.y == (__int128_t)a.y * b.x; }
friend bool operator ==(Frac a, int b) { return a.x == (__int128_t)a.y * b; }
friend bool operator !=(Frac a, int b) { return a.x != (__int128_t)a.y * b; }
f64 real() { return (f64)x / y; }
};
int n, m;
int p[kMaxN], sz[kMaxN], wson[kMaxN], top[kMaxN], dep[kMaxN], dfn[kMaxN];
f64 ans = 1e18;
Frac a[kMaxN][4];
std::vector<int> G[kMaxN];
std::vector<std::pair<int, int>> upd[kMaxN * 2];
std::vector<std::tuple<Frac, Frac, Frac, Frac>> vec[kMaxN * 2];
// [初始位置,出現時間,結束時間,速度]
void dfs1(int u, int fa) {
p[u] = fa, dep[u] = dep[fa] + 1, sz[u] = 1;
for (auto v : G[u]) {
if (v == fa) continue;
dfs1(v, u);
sz[u] += sz[v];
if (sz[v] > sz[wson[u]]) wson[u] = v;
}
}
void dfs2(int u, int fa, int t) {
static int cnt = 0;
top[u] = t;
if (wson[u]) dfs2(wson[u], u, t);
for (auto v : G[u]) {
if (v == fa || v == wson[u]) continue;
dfs2(v, u, v);
}
}
void prework() {
dfs1(1, 0), dfs2(1, 0, 1);
}
void work(int t, int c, int u, int v) {
std::vector<std::tuple<int, int, int>> vecu, vecv;
for (; top[u] != top[v];) {
if (dep[top[u]] >= dep[top[v]]) {
vecu.emplace_back(top[u], dep[u] - dep[top[u]], 0);
vecu.emplace_back(top[u] + n, 1, 0);
u = p[top[u]];
} else {
vecv.emplace_back(top[v], 0, dep[v] - dep[top[v]]);
vecv.emplace_back(top[v] + n, 0, 1);
v = p[top[v]];
}
}
if (dep[u] <= dep[v])
vecv.emplace_back(top[u], dep[u] - dep[top[u]], dep[v] - dep[top[u]]);
else
vecu.emplace_back(top[u], dep[u] - dep[top[u]], dep[v] - dep[top[u]]);
std::reverse(vecv.begin(), vecv.end());
for (auto p : vecv) vecu.emplace_back(p);
Frac now = Frac(t, 1);
for (auto [id, l, r] : vecu) {
vec[id].emplace_back(Frac(l, 1), now, now + Frac(abs(l - r), c), Frac(l <= r ? c : -c, 1));
now = now + Frac(abs(l - r), c);
}
}
int getid(std::vector<Frac> &vec, Frac x) {
int L = -1, R = vec.size(), res = -1;
while (L + 1 < R) {
int mid = (L + R) >> 1;
if (x <= vec[mid]) R = res = mid;
else L = mid;
}
return res;
}
Frac calc(int x, int y) {
Frac s = a[x][3] * a[x][1] - a[y][3] * a[y][1] + a[y][0] - a[x][0];
Frac v = a[x][3] - a[y][3];
if (v == 0 && !(s == 0)) {
return Frac(1e14, 1);
} else if (v == 0) {
if (std::max(a[x][1], a[y][1]) <= std::min(a[x][2], a[y][2])) return std::max(a[x][1], a[y][1]);
else return Frac(1e14, 1);
} else {
Frac t = s / v;
if (t >= a[x][1] && t >= a[y][1] && t <= a[x][2] && t <= a[y][2]) return t;
else return Frac(1e14, 1);
}
}
void solve(int x) {
std::vector<Frac> tmp, unq;
int tot = 0;
Frac nowt = Frac(0, 1);
auto cmp = [&] (int i, int j) -> bool {
return a[i][0] + a[i][3] * (nowt - a[i][1]) < a[j][0] + a[j][3] * (nowt - a[j][1]);
};
for (auto [p, l, r, c] : vec[x]) {
++tot;
a[tot][0] = p, a[tot][1] = l, a[tot][2] = r, a[tot][3] = c;
tmp.emplace_back(l), tmp.emplace_back(r);
}
std::sort(tmp.begin(), tmp.end());
for (int i = 0; i < (int)tmp.size(); ++i) {
if (!i) unq.emplace_back(tmp[i]);
else if (!(tmp[i] == tmp[i - 1])) unq.emplace_back(tmp[i]);
}
for (int i = 0; i < (int)unq.size(); ++i) upd[i].clear();
for (int i = 1; i <= tot; ++i) {
upd[getid(unq, a[i][1])].emplace_back(i, 1);
upd[getid(unq, a[i][2])].emplace_back(i, -1);
}
std::set<int, decltype(cmp)> st(cmp);
Frac res = Frac(1e12, 1);
for (int i = 0; i < (int)unq.size(); ++i) {
nowt = unq[i];
if (res < nowt) break;
for (auto [x, v] : upd[i]) {
if (v == 1) {
auto it = st.lower_bound(x);
if (it != st.end()) {
Frac val = calc(*it, x);
res = std::min(res, val);
if (res < nowt) return void(ans = std::min(ans, res.real()));
}
if (it != st.begin()) {
--it;
Frac val = calc(*it, x);
res = std::min(res, val);
if (res < nowt) return void(ans = std::min(ans, res.real()));
}
st.emplace(x);
}
}
for (auto [x, v] : upd[i]) {
if (v == -1) st.erase(x);
}
}
ans = std::min(ans, res.real());
}
void dickdreamer() {
std::cin >> n >> m;
for (int i = 1; i < n; ++i) {
int u, v;
std::cin >> u >> v;
G[u].emplace_back(v), G[v].emplace_back(u);
}
prework();
for (int i = 1; i <= m; ++i) {
int t, c, u, v;
std::cin >> t >> c >> u >> v;
work(t, c, u, v);
}
for (int i = 1; i <= 2 * n; ++i) solve(i);
if (ans >= 1e10) std::cout << "-1\n";
else std::cout << std::fixed << std::setprecision(10) << ans << '\n';
}
int32_t main() {
#ifdef ORZXKR
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int T = 1;
// std::cin >> T;
while (T--) dickdreamer();
// std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
return 0;
}
float128 實現
#include <bits/stdc++.h>
// #define int int64_t
using f64 = long double;
using f128 = __float128;
const int kMaxN = 1e5 + 5;
const f128 kEps = 1e-7;
int n, m;
int p[kMaxN], sz[kMaxN], wson[kMaxN], top[kMaxN], dep[kMaxN], dfn[kMaxN];
f128 ans = 1e18, a[kMaxN][4];
std::vector<int> G[kMaxN];
std::vector<std::pair<int, int>> upd[kMaxN * 2];
std::vector<std::tuple<f128, f128, f128, f128>> vec[kMaxN * 2];
// [初始位置,出現時間,結束時間,速度]
void dfs1(int u, int fa) {
p[u] = fa, dep[u] = dep[fa] + 1, sz[u] = 1;
for (auto v : G[u]) {
if (v == fa) continue;
dfs1(v, u);
sz[u] += sz[v];
if (sz[v] > sz[wson[u]]) wson[u] = v;
}
}
void dfs2(int u, int fa, int t) {
static int cnt = 0;
top[u] = t, dfn[u] = ++cnt;
if (wson[u]) dfs2(wson[u], u, t);
for (auto v : G[u]) {
if (v == fa || v == wson[u]) continue;
dfs2(v, u, v);
}
}
void prework() {
dfs1(1, 0), dfs2(1, 0, 1);
}
void work(int t, int c, int u, int v) {
std::vector<std::tuple<int, int, int>> vecu, vecv;
for (; top[u] != top[v];) {
if (dep[top[u]] >= dep[top[v]]) {
vecu.emplace_back(top[u], dep[u] - dep[top[u]], 0);
vecu.emplace_back(top[u] + n, 1, 0);
u = p[top[u]];
} else {
vecv.emplace_back(top[v], 0, dep[v] - dep[top[v]]);
vecv.emplace_back(top[v] + n, 0, 1);
v = p[top[v]];
}
}
if (dep[u] <= dep[v])
vecv.emplace_back(top[u], dep[u] - dep[top[u]], dep[v] - dep[top[u]]);
else
vecu.emplace_back(top[u], dep[u] - dep[top[u]], dep[v] - dep[top[u]]);
std::reverse(vecv.begin(), vecv.end());
for (auto p : vecv) vecu.emplace_back(p);
f128 now = t;
for (auto [id, l, r] : vecu) {
vec[id].emplace_back(l, now, now + fabs(l - r) / c, l <= r ? c : -c);
now += fabs(l - r) / c;
}
}
int getid(std::vector<f128> &vec, f128 x) {
int L = -1, R = vec.size(), res = -1;
while (L + 1 < R) {
int mid = (L + R) >> 1;
if (x <= vec[mid] + kEps) R = res = mid;
else L = mid;
}
return res;
}
f128 calc(int x, int y) {
f128 s = a[x][3] * a[x][1] - a[y][3] * a[y][1] + a[y][0] - a[x][0];
f128 v = a[x][3] - a[y][3];
if (fabs(v) <= kEps && fabs(s) > kEps) return 1e18;
else if (fabs(v) <= kEps) {
if (std::max(a[x][1], a[y][1]) <= std::min(a[x][2], a[y][2]) + kEps) return std::max(a[x][1], a[y][1]);
else return 1e18;
} else {
f128 t = s / v;
if (t >= a[x][1] - kEps && t >= a[y][1] - kEps && t <= a[x][2] + kEps && t <= a[y][2] + kEps) return t;
else return 1e18;
}
}
void solve(int x) {
static std::vector<f128> tmp, unq;
tmp.clear(), tmp.shrink_to_fit();
unq.clear(), unq.shrink_to_fit();
int tot = 0;
f128 nowt = 0;
auto cmp = [&] (int i, int j) -> bool {
return a[i][0] + a[i][3] * (nowt - a[i][1]) < a[j][0] + a[j][3] * (nowt - a[j][1]);
};
for (auto [p, l, r, c] : vec[x]) {
++tot;
a[tot][0] = p, a[tot][1] = l, a[tot][2] = r, a[tot][3] = c;
tmp.emplace_back(l), tmp.emplace_back(r);
}
std::sort(tmp.begin(), tmp.end());
for (int i = 0; i < (int)tmp.size(); ++i) {
if (!i) unq.emplace_back(tmp[i]);
else if (fabs(tmp[i] - tmp[i - 1]) > kEps) unq.emplace_back(tmp[i]);
}
for (int i = 0; i < (int)unq.size(); ++i) upd[i].clear();
for (int i = 1; i <= tot; ++i) {
upd[getid(unq, a[i][1])].emplace_back(i, 1);
upd[getid(unq, a[i][2])].emplace_back(i, -1);
}
std::set<int, decltype(cmp)> st(cmp);
f128 res = 1e18;
for (int i = 0; i < (int)unq.size(); ++i) {
nowt = unq[i];
if (res < nowt - kEps) break;
for (auto [x, v] : upd[i]) {
if (v == 1) {
auto it = st.lower_bound(x);
if (it != st.end()) {
f128 val = calc(*it, x);
res = std::min(res, val);
if (res < nowt - kEps) return void(ans = std::min(ans, res));
}
if (it != st.begin()) {
--it;
f128 val = calc(*it, x);
res = std::min(res, val);
if (res < nowt - kEps) return void(ans = std::min(ans, res));
}
st.emplace(x);
}
}
for (auto [x, v] : upd[i]) {
if (v == -1) st.erase(x);
}
}
ans = std::min(ans, res);
}
void dickdreamer() {
std::cin >> n >> m;
for (int i = 1; i < n; ++i) {
int u, v;
std::cin >> u >> v;
G[u].emplace_back(v), G[v].emplace_back(u);
}
prework();
for (int i = 1; i <= m; ++i) {
int t, c, u, v;
std::cin >> t >> c >> u >> v;
work(t, c, u, v);
}
for (int i = 1; i <= 2 * n; ++i) solve(i);
if (ans >= 1e10) std::cout << "-1\n";
else std::cout << std::fixed << std::setprecision(10) << (f64)ans << '\n';
}
int32_t main() {
#ifdef ORZXKR
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int T = 1;
// std::cin >> T;
while (T--) dickdreamer();
// std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
return 0;
}