[BZOJ 1758] [Wc2010]重建计划

[Wc2010]重建计划

BZOJ 1758

参考博客

https://blog.bill.moe/WC2010-rebuild/

1

题目大意

给出一个 \(n\) 个节点带边权的树,要求找出一条边数在 \([L,U]\) 之间的路径使得 \(\dfrac{\sum_{e\in S}e(w)}{|S|}\) 最大

数据范围

\(n\le100000,1\le L\le U\le n-1,V_i\le1000000\)

时空限制

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\n", solve());
	return 0;
}
posted @ 2019-01-10 16:47  LJZ_C  阅读(98)  评论(0编辑  收藏  举报