【题目链接】
【前置技能】
- 二分答案
- 单调队列
- 点分治
【题解】
- 先二分答案 ,将每条边的权值减去 ,问题转化为判断在树上是否存在一条长度在 到 之间的路径,其权值和非负。
- 路径问题,用点分治处理。记录下每个分治中心出发的各个长度的路径的最大权值。在处理分治中心的某个子树的时候,在已经处理出来的数组中用单调队列查询,然后将这棵子树的信息合并进数组。
- 分析一下复杂度,发现在一些情况下可能会退化。举个极端的例子,分治中心处第一棵被访问到的子树的深度为 ,以后的每一棵子树都只有一个点,那么每一次的单调队列都会把R个元素都访问一遍,点分治的时间复杂度退化为 。将子树按深度从小到大排序,就避免了这个问题。
- 可以将点分树存起来,预先把子树排个序,卡一卡常数。注意排序要 基数排序,不然复杂度多一个 ,而且还不如直接线段树维护一发写得方便。
- 时间复杂度 。
- 或者如果觉得这样处理来处理去太麻烦的话,也可以选择长链剖分。
【代码】
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define LL long long
#define MAXN 100010
#define EPS 1e-4
using namespace std;
int n, L, R, fla, ROOT, arrsize, cursize;
int size[MAXN], wei[MAXN], root, vis[MAXN], dep[MAXN];
double maxw, f[MAXN], cur[MAXN];
struct info{int v; double w;};
vector <info> a[MAXN], c[MAXN];
vector <int> b[MAXN];
template <typename T> void chkmin(T &x, T y){x = min(x, y);}
template <typename T> void chkmax(T &x, T y){x = max(x, y);}
template <typename T> void read(T &x){
x = 0; int f = 1; char ch = getchar();
while (!isdigit(ch)) {if (ch == '-') f = -1; ch = getchar();}
while (isdigit(ch)) {x = x * 10 + ch - '0'; ch = getchar();}
x *= f;
}
void getroot(int pos, int dad, int tot){
wei[pos] = 0, size[pos] = 1, dep[pos] = 0;
for (unsigned i = 0, si = a[pos].size(); i < si; ++i){
int son = a[pos][i].v;
if (son != dad && !vis[son]){
getroot(son, pos, tot);
size[pos] += size[son];
chkmax(wei[pos], size[son]);
chkmax(dep[pos], dep[son]);
}
}
++dep[pos];
chkmax(wei[pos], tot - size[pos]);
if (wei[pos] < wei[root]) root = pos;
}
void init(int pos, int tot){
vis[pos] = 1;
root = 0; getroot(pos, 0, tot);
static int cnt[MAXN], rk[MAXN], tmpv[MAXN], maxn, sizeson;
static double tmpw[MAXN];
maxn = sizeson = 0;
for (unsigned i = 0, si = a[pos].size(); i < si; ++i){
int son = a[pos][i].v;
if (!vis[son]){
++sizeson;
chkmax(maxn, dep[son]);
++cnt[dep[son]];
}
}
for (int i = 1; i <= maxn; ++i)
cnt[i] += cnt[i - 1];
for (int i = 0, si = a[pos].size(); i < si; ++i){
int son = a[pos][i].v;
if (!vis[son]) rk[cnt[dep[son]]--] = i;
}
for (int i = 1; i <= sizeson; ++i)
tmpv[i] = a[pos][rk[i]].v, tmpw[i] = a[pos][rk[i]].w;
for (int i = 1; i <= sizeson; ++i)
c[pos].push_back((info){tmpv[i], (double)tmpw[i]});
for (int i = 1; i <= maxn; ++i)
cnt[i] = 0;
for (unsigned i = 0, si = a[pos].size(); i < si; ++i){
int son = a[pos][i].v;
if (!vis[son]){
root = 0; getroot(son, 0, size[son]);
b[pos].push_back(root);
init(root, size[son]);
}
}
}
void getans(int pos, int dad, double val, int dep){
chkmax(cur[dep], val);
chkmax(cursize, dep);
for (unsigned i = 0, si = a[pos].size(); i < si; ++i){
int son = a[pos][i].v; double wth = a[pos][i].w;
if (!vis[son] && son != dad){
getans(son, pos, val + wth, dep + 1);
}
}
}
void work(int pos){
if (fla) return;
vis[pos] = 1;
arrsize = 0;
for (unsigned i = 0, si = c[pos].size(); i < si; ++i){
int son = c[pos][i].v; double wth = c[pos][i].w;
if (!vis[son]){
cursize = 0;
getans(son, 0, wth, 1);
static int q[MAXN], l, r;
l = 0, r = -1;
for (int j = min(arrsize, R - 1); j >= max(0, L - 1); --j){
while (l <= r && f[q[r]] < f[j]) --r;
q[++r] = j;
}
for (int j = 1; j <= cursize; ++j){
while (l <= r && q[l] > R - j) ++l;
while (L - j >= 0 && L - j <= arrsize && l <= r && f[q[r]] < f[L - j]) --r;
q[++r] = L - j;
if (l <= r && cur[j] + f[q[l]] >= 0) fla = 1;
}
chkmax(arrsize, cursize);
for (int j = 1; j <= arrsize; ++j)
chkmax(f[j], cur[j]), cur[j] = -INF;
}
}
for (int i = 1; i <= arrsize; ++i)
f[i] = -INF;
for (unsigned i = 0, si = b[pos].size(); i < si; ++i)
work(b[pos][i]);
}
bool ok(double x){
fla = 0;
for (int i = 1; i <= n; ++i)
for (int j = 0, si = a[i].size(); j < si; ++j)
a[i][j].w -= x;
for (int i = 1; i <= n; ++i)
for (int j = 0, si = c[i].size(); j < si; ++j)
c[i][j].w -= x;
memset(vis, 0, sizeof(vis));
for (int i = 1; i <= n; ++i)
f[i] = cur[i] = -INF;
work(ROOT);
for (int i = 1; i <= n; ++i)
for (int j = 0, si = a[i].size(); j < si; ++j)
a[i][j].w += x;
for (int i = 1; i <= n; ++i)
for (int j = 0, si = c[i].size(); j < si; ++j)
c[i][j].w += x;
return fla;
}
int main(){
read(n), read(L), read(R);
for (int i = 1; i < n; ++i){
int u, v, w; read(u), read(v), read(w);
chkmax(maxw, (double)w);
a[u].push_back((info){v, (double)w});
a[v].push_back((info){u, (double)w});
}
memset(vis, 0, sizeof(vis));
size[0] = wei[0] = n;
root = 0; getroot(1, 0, n); ROOT = root;
init(root, n);
double l = 0, r = maxw;
while (l + EPS < r){
double mid = (l + r) / 2;
if (ok(mid)) l = mid;
else r = mid;
}
printf("%.3lf\n", (l + r) / 2);
return 0;
}