Address
Solution
- 容易想到二分答案
mid
m
i
d
,将树上每条边减去
mid
m
i
d
,原问题被转化为求树上是否存在一条价值和大于等于
0
0
的路径,可以用点分治来做。
- 因为题目对路径长度有限制,记录之前处理的子树中某一长度的路径价值和的最大值。
- 对重心的每一棵子树 BFS,则按照
BFS
B
F
S
序枚举点,路径的长度递增。
- 枚举之前处理的子树中的路径长度,则对于当前子树可以用单调队列(滑动窗口)来维护对应的最大值,每次判断路径总价值和是否大于等于
0
0
即可。
- 时间复杂度 O(nlogn×二分次数)。
尽管洛谷跑得很慢,BZOJ那么卡常竟然过了……- 如果有什么好的提高运行速度的方法欢迎指出。
Code
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
const double eps = 1e-6;
const int Maxn = 0x3f3f3f3f;
const int N = 1e5 + 5, M = N << 1;
int n, L, U, Gs, Gt, tis, last, top, num;
int sze[N], len[N], G[N], h[N], ph[N];
bool flag, vis[N], stp[N];
double l = 1e9, r = -1e9, Ans, dis[N], f[N];
struct Edge
{
int to; double cst; Edge *nxt;
}p[M], *lst[N], *P = p;
inline void Link(int x, int y, int z)
{
(++P)->nxt = lst[x]; lst[x] = P; P->to = y; P->cst = z;
(++P)->nxt = lst[y]; lst[y] = P; P->to = x; P->cst = z;
if (z < l) l = z; if (z > r) r = z;
}
inline int get()
{
char ch; int res = 0; bool flag = false;
while (ch = getchar(), !isdigit(ch) && ch != '-');
(ch == '-' ? flag = true : res = ch ^ 48);
while (ch = getchar(), isdigit(ch))
res = res * 10 + ch - 48;
return flag ? -res : res;
}
template <class T> inline void CkMax(T &x, T y) {if (x < y) x = y;}
inline int Min(int x, int y) {return x < y ? x : y;}
inline void Dfs1(int x, int fa)
{
int cnt = 0; sze[x] = 1;
for (Edge *e = lst[x]; e; e = e->nxt)
{
int y = e->to;
if (y == fa || vis[y]) continue;
Dfs1(y, x);
sze[x] += sze[y];
CkMax(cnt, sze[y]);
}
CkMax(cnt, tis - sze[x]);
if (cnt < Gs)
Gs = cnt, Gt = x;
}
inline int findG(int x)
{
Gs = Maxn;
Dfs1(x, 0);
return Gt;
}
inline void Init(int x)
{
int y; G[++num] = findG(x); vis[G[num]] = true;
for (Edge *e = lst[G[num]]; e; e = e->nxt)
if (!vis[y = e->to]) tis = sze[y], Init(y);
}
inline void Bfs(int src)
{
stp[h[++top] = src] = true; int x, y;
for (int i = last + 1; i <= top; ++i)
{
x = h[i];
for (Edge *e = lst[x]; e; e = e->nxt)
if (!vis[y = e->to] && !stp[y])
{
dis[y] = dis[x] + e->cst;
len[y] = len[x] + 1;
stp[h[++top] = y] = true;
}
}
for (int i = last + 1; i <= top; ++i)
stp[h[i]] = false;
}
inline void Check()
{
int t = 1, w = 0, tip = last + 1;
for (int i = Min(U, len[h[top]]); i >= 0; --i)
{
int tl = i >= L ? 0 : L - i, tr = U - i;
while (t <= w && len[ph[t]] < tl) ++t;
while (tip <= top && len[h[tip]] < tl) ++tip;
while (tip <= top && len[h[tip]] <= tr)
{
while (t <= w && dis[ph[w]] + eps <= dis[h[tip]]) --w;
ph[++w] = h[tip++];
}
if (t <= w && f[i] + dis[ph[t]] >= -eps)
return (void)(flag = true);
}
}
inline void solve(int x)
{
int Gz = G[++num], y;
vis[Gz] = true; h[0] = Gz;
f[0] = dis[Gz] = len[Gz] = 0; top = 0;
for (Edge *e = lst[Gz]; e; e = e->nxt)
if (!vis[y = e->to])
{
dis[y] = e->cst; len[y] = 1;
last = top; Bfs(y); Check();
for (int i = last + 1; i <= top; ++i)
CkMax(f[len[h[i]]], dis[h[i]]);
}
for (int i = 0; i <= top; ++i)
f[len[h[i]]] = -1e9;
for (Edge *e = lst[Gz]; e; e = e->nxt)
if (!vis[y = e->to]) solve(y);
}
inline bool Judge(double mid)
{
memset(vis, false, sizeof(vis));
for (int i = 1; i <= n; ++i)
for (Edge *e = lst[i]; e; e = e->nxt)
e->cst -= mid;
flag = false; num = 0; solve(1);
for (int i = 1; i <= n; ++i)
for (Edge *e = lst[i]; e; e = e->nxt)
e->cst += mid;
return flag;
}
int main()
{
tis = n = get(); L = get(); U = get(); int x, y;
for (int i = 1; i < n; ++i)
{
x = get(); y = get();
Link(x, y, get()); f[i] = -1e9;
}
Init(1);
for (int i = 1; i <= 32; ++i)
{
double mid = (l + r) / 2.0;
if (Judge(mid)) Ans = mid, l = mid;
else r = mid;
}
printf("%.3lf", Ans);
}