【CodeForces704E】Iron Man

【题目链接】

【思路要点】

  • 考虑链上做法,每个人的坐标是一个关于时间的一次函数。
  • 注意到当且仅当两人相碰,两人位置的相对顺序会发生改变,换言之,在两人相碰之前,所有人位置的相对顺序不变。
  • 排序所有人出现,消失的事件,用平衡树维护所有人的相对位置即可。
  • 回到原题,对原树进行树链剖分,对每条重链和轻边都运行链上做法即可。
  • 时间复杂度 O ( N + M L o g 2 N ) O(N+MLog^2N) O(N+MLog2N)
  • 本题中,涉及除法的地方不多,分子和分母的乘积始终在六十四位整型范围内,可以通过手写有理数类来避免实数运算。

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
const int INF = 2e4;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
struct frac {ll x, y; };
ll absll(ll x) {return x >= 0 ? x : -x; }
frac make(int x) {return (frac) {x, 1}; }
frac normal(frac x) {
	ll g = __gcd(absll(x.x), absll(x.y));
	if (x.y < 0) g = -g;
	return (frac) {x.x / g, x.y / g};
}
frac operator + (frac a, frac b) {return normal((frac) {a.x * b.y + a.y * b.x, a.y * b.y}); }
frac operator - (frac a, frac b) {return normal((frac) {a.x * b.y - a.y * b.x, a.y * b.y}); }
frac operator * (frac a, frac b) {return normal((frac) {a.x * b.x, a.y * b.y}); }
frac operator / (frac a, frac b) {return normal((frac) {a.x * b.y, a.y * b.x}); }
bool operator < (frac a, frac b) {return a.x * b.y < b.x * a.y; }
bool operator > (frac a, frac b) {return a.x * b.y > b.x * a.y; }
bool operator <= (frac a, frac b) {return a.x * b.y <= b.x * a.y; }
bool operator >= (frac a, frac b) {return a.x * b.y >= b.x * a.y; }
bool operator == (frac a, frac b) {return a.x * b.y == b.x * a.y; }
int n, m, depth[MAXN], size[MAXN], son[MAXN];
int timer, father[MAXN], dfn[MAXN], up[MAXN];
vector <int> a[MAXN]; double ans;
int lca(int x, int y) {
	while (up[x] != up[y]) {
		if (depth[up[x]] < depth[up[y]]) swap(x, y);
		x = father[up[x]];
	}
	if (depth[x] < depth[y]) return x;
	else return y;
}
frac dist(int x, int y) {
	int z = lca(x, y);
	return (frac) {depth[x] + depth[y] - 2 * depth[z], 1};
}
struct event {frac k, b, l, r; };
frac Timer, Goal;
bool operator < (event a, event b) {
	if (a.k * Timer + a.b == b.k * Timer + b.b) {
		if (a.l == b.l) {
			if (a.r == b.r) return a.k < b.k;
			else return a.r < b.r;
		} else return a.l < b.l;
	} else return a.k * Timer + a.b < b.k * Timer + b.b;
}
multiset <event> st;
bool cmp(pair <event, bool> x, pair <event, bool> y) {
	frac tx = x.second ? x.first.l : x.first.r;
	frac ty = y.second ? y.first.l : y.first.r;
	if (tx == ty) return x.second > y.second;
	else return tx < ty;
}
frac inter(event a, event b) {
	if (a.k == b.k) {
		if (a.b == b.b) return max(a.l, b.l);
		else return make(INF);
	}
	frac res = (b.b - a.b) / (a.k - b.k);
	if (res >= max(a.l, b.l) && res <= min(a.r, b.r)) return res;
	else return make(INF);
}
void work(vector <event> &a) {
	st.clear(), Goal = make(INF);
	vector <pair <event, bool>> b;
	for (auto x : a) {
		b.emplace_back(x, true);
		b.emplace_back(x, false);
	}
	sort(b.begin(), b.end(), cmp);
	for (auto x : b) {
		frac now = x.second ? x.first.l : x.first.r;
		if (now >= Goal) {
			chkmin(ans, 1.0 * Goal.x / Goal.y);
			return;
		}
		Timer = now;
		if (x.second) {
			multiset <event> :: iterator tmp = st.insert(x.first);
			multiset <event> :: iterator pre = tmp, suf = tmp;
			if (pre != st.begin()) {
				pre--;
				chkmin(Goal, inter(*pre, *tmp));
			}
			if (++suf != st.end()) chkmin(Goal, inter(*tmp, *suf));
		} else {
			multiset <event> :: iterator tmp = st.lower_bound(x.first);
			multiset <event> :: iterator pre = tmp, suf = tmp;
			if (++suf != st.end() && pre != st.begin()) {
				pre--;
				chkmin(Goal, inter(*pre, *suf));
			}
			st.erase(tmp);
		}
	}
	chkmin(ans, 1.0 * Goal.x / Goal.y);
}
vector <event> heavy[MAXN], light[MAXN];
void addquery(int x, int y, frac s, frac v) {
	frac t = s + dist(x, y) / v;
	while (up[x] != up[y]) {
		if (depth[up[x]] > depth[up[y]]) {
			int f = up[x];
			heavy[f].push_back((event) {make(0) - v, make(depth[x]) + v * s, s, s + make(depth[x] - depth[f]) / v});
			s = s + make(depth[x] - depth[f]) / v;
			x = up[x], f = father[x];
			light[x].push_back((event) {make(0) - v, make(depth[x]) + v * s, s, s + make(depth[x] - depth[f]) / v});
			s = s + make(depth[x] - depth[f]) / v;
			x = father[x];
		} else {
			int f = up[y];
			heavy[f].push_back((event) {v, make(depth[y]) - v * t, t - make(depth[y] - depth[f]) / v, t});
			t = t - make(depth[y] - depth[f]) / v;
			y = up[y], f = father[y];
			light[y].push_back((event) {v, make(depth[y]) - v * t, t - make(depth[y] - depth[f]) / v, t});
			t = t - make(depth[y] - depth[f]) / v;
			y = father[y];
		}
	}
	int f = up[x];
	if (depth[x] > depth[y]) heavy[f].push_back((event) {make(0) - v, make(depth[x]) + v * s, s, t});
	else heavy[f].push_back((event) {v, make(depth[y]) - v * t, s, t});
}
void dfs(int pos, int fa) {
	depth[pos] = depth[fa] + 1;
	size[pos] = 1, son[pos] = 0;
	for (auto x : a[pos])
		if (x != fa) {
			dfs(x, pos);
			size[pos] += size[x];
			if (size[x] > size[son[pos]]) son[pos] = x;
		}
}
void efs(int pos, int fa, int from) {
	up[pos] = from;
	father[pos] = fa;
	dfn[pos] = ++timer;
	if (son[pos]) efs(son[pos], pos, from);
	for (auto x : a[pos])
		if (x != son[pos] && x != fa)
			efs(x, pos, x);
}
int main() {
	read(n), read(m);
	for (int i = 1; i <= n - 1; i++) {
		int x, y; read(x), read(y);
		a[x].push_back(y);
		a[y].push_back(x);
	}
	dfs(1, 0);
	efs(1, 0, 1);
	for (int i = 1; i <= m; i++) {
		frac t, v; int x, y;
		read(t.x), t.y = 1;
		read(v.x), v.y = 1;
		read(x), read(y);
		addquery(x, y, t, v);
	}
	ans = INF;
	for (int i = 1; i <= n; i++) {
		work(heavy[i]);
		work(light[i]);
	}
	if (ans >= INF) puts("-1");
	else printf("%.10lf\n", ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值