[Wc2010]重建计划
参考博客
题目大意
给出一个 (n) 个节点带边权的树,要求找出一条边数在 ([L,U]) 之间的路径使得 (dfrac{sum_{ein S}e(w)}{|S|}) 最大
数据范围
(nle100000,1le Lle Ule n-1,V_ile1000000)
时空限制
40sec,128MB
分析
首先01分数规划转判断问题,现在我们每个边有新的边权,我们要求树上边权最大的一条边数 ([L,U]) 的路径
因为与深度有关,可以使用长链剖分解决,现在要用线段树维护一个区间最值,那么之前的指针实现移位就无法使用了,考虑数组只会向右移位(转移方程为 (f(v,j)->f(u,j+1))),那么可以采用另一种移位方法,将树剖分成链,那么(f(u,j)) 的值就存储在 (val(dfn[u]+j)) 位置,对 (val) 数组维护一个线段树就好了
Code
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
inline char nc() {
static char buf[100000], *l = buf, *r = buf;
return l==r&&(r=(l=buf)+fread(buf,1,100000,stdin),l==r)?EOF:*l++;
}
template<class T> void read(T & x) {
x = 0; int f = 1, ch = nc();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=nc();}
while(ch>='0'&&ch<='9'){x=x*10-'0'+ch;ch=nc();}
x *= f;
}
#define lson u << 1, l, mid
#define rson u << 1 | 1, mid + 1, r
const double eps = 1e-5;
const double infty = 1e18;
const int maxn = 100000 + 5;
const int maxe = maxn * 2;
const int maxv = 1000000 + 5;
int N, L, U; double an;
int head[maxn], ecnt;
double mid;
int dfn[maxn], len[maxn], son[maxn], cost[maxn], dfc;
double r[maxn];
struct edge {
int to, nex, cost;
edge(int to=0,int nex=0,int cost=0) : to(to), nex(nex), cost(cost) {}
} G[maxe];
struct segment_tree {
double mx[maxn << 2];
inline void pushup(int u) {
mx[u] = max(mx[u << 1], mx[u << 1 | 1]);
}
void build(int u, int l, int r) {
if(l == r) {
mx[u] = -infty;
return;
}
int mid = (l + r) >> 1;
build(lson), build(rson);
pushup(u);
}
void update(int u, int l, int r, int qp, double qv) {
if(l == r) {
mx[u] = max(mx[u], qv);
return;
}
int mid = (l + r) >> 1;
if(qp <= mid) update(lson, qp, qv);
else update(rson, qp, qv);
pushup(u);
}
double query(int u, int l, int r, int ql, int qr) {
if(l == ql && r == qr) {
return mx[u];
}
int mid = (l + r) >> 1;
if(qr <= mid) return query(lson, ql, qr);
else if (ql > mid) return query(rson, ql, qr);
else {
double L = query(lson, ql, mid);
double R = query(rson, mid + 1, qr);
return max(L, R);
}
}
} seg;
inline void addedge(int u, int v, int w) {
G[ecnt] = edge(v, head[u], w), head[u] = ecnt++;
G[ecnt] = edge(u, head[v], w), head[v] = ecnt++;
}
void dfs1(int u, int fa) {
son[u] = -1;
for(int i = head[u]; ~ i; i = G[i].nex) {
int v = G[i].to; if(v == fa) continue;
dfs1(v, u);
if(son[u] == -1 || len[son[u]] < len[v]) {
son[u] = v; cost[u] = G[i].cost;
len[u] = len[v] + 1;
}
}
}
void dfs2(int u, int fa) {
dfn[u] = ++dfc;
if(son[u] != -1) {
dfs2(son[u], u);
}
for(int i = head[u]; ~ i; i = G[i].nex) {
int v = G[i].to; if(v == fa || v == son[u]) continue;
dfs2(v, u);
}
}
double query(int u, int l, int r) {
l = max(l, 0), r = min(r, len[u]);
if(l > r) return -infty;
return seg.query(1, 1, N, dfn[u] + l, dfn[u] + r);
}
void dp(int u, int fa, double dis) {
if(son[u] != -1) {
dp(son[u], u, dis + cost[u] - mid);
}
seg.update(1, 1, N, dfn[u], dis);
for(int i = head[u]; ~ i; i = G[i].nex) {
int v = G[i].to; if(v == fa || v == son[u]) continue;
dp(v, u, dis + G[i].cost - mid);
for(int j = 0; j <= len[v]; ++j) {
r[j] = seg.query(1, 1, N, dfn[v] + j, dfn[v] + j);
an = max(an, r[j] + query(u, L - j - 1, U - j - 1) - dis * 2);
}
for(int j = 0; j <= len[v]; ++j) {
seg.update(1, 1, N, dfn[u] + j + 1, r[j]);
}
}
an = max(an, query(u, L, U) - dis);
}
bool judge() {
an = -infty;
seg.build(1, 1, N);
dp(1, 0, 0);
return an > -eps;
}
double solve() {
double l = 0, r = maxv;
while(r - l > eps)
{
mid = (l + r) / 2;
if(judge()) l = mid;
else r = mid;
}
return l;
}
int main() {
// freopen("1.txt", "r", stdin);
read(N), read(L), read(U);
memset(head, -1, sizeof(head));
for(int i = 1; i < N; ++i) {
int A, B, V; read(A), read(B), read(V);
addedge(A, B, V);
}
dfs1(1, 0), dfs2(1, 0);
printf("%.3lf
", solve());
return 0;
}