Bzoj1758: [Wc2010]重建计划

#题面
传送门

Sol

题意就是给你一棵树,有边权
求边数在\([L, U]\)内的一条路径,使得边权和除以边数最大,输出这个最大值


二分答案+点分治+单调队列
二分一个答案\(mid\),把所有的边权减去这个\(mid\)就是\(check\)是否有一条边数满足要求的大于等于零的路径
\(bfs\)求出当前每个点到根的\(dis\)\(deep\),使其递增
记录下每个深度最大的\(dis\),维护这个的递减的单调队列即可

注意常数优化!!!

奉上加了一组数据后的\(TLE\)代码供参考加什么数据咯
然后博主不想改

# include <bits/stdc++.h>
# define RG register
# define IL inline
# define Fill(a, b) memset(a, b, sizeof(a))
using namespace std;
typedef long long ll;
const int _(1e5 + 5);
const double EPS(1e-4);

IL int Input(){
    RG int x = 0, z = 1; RG char c = getchar();
    for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
    for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
    return x * z;
}

int n, L, U, first[_], cnt, mx[_], root, size[_], vis[_], tot;
int mark[_], tmp, deep[_];
double ans, dis[_], t[_];
struct Edge{
    int to, w, next;
} edge[_ << 1];
int head, tail, Q[_], deq[_];

IL void Add(RG int u, RG int v, RG int w){
    edge[cnt] = (Edge){v, w, first[u]}, first[u] = cnt++;
}

IL void GetRoot(RG int u, RG int ff){
    size[u] = 1, mx[u] = 0;
    for(RG int e = first[u]; e != -1; e = edge[e].next){
        RG int v = edge[e].to;
        if(vis[v] || v == ff) continue;
        GetRoot(v, u);
        size[u] += size[v];
        mx[u] = max(mx[u], size[v]);
    }
    mx[u] = max(mx[u], tot - size[u]);
    if(mx[u] < mx[root]) root = u;
}

IL void Bfs(RG double mid, RG int x){
	Q[head = tail = 0] = x, mark[x] = 1;
	while(head <= tail){
		RG int u = Q[head++];
		for(RG int e = first[u]; e != -1; e = edge[e].next){
			RG int v = edge[e].to, w = edge[e].w;
			if(vis[v] || mark[v]) continue;
			deep[v] = deep[u] + 1, dis[v] = dis[u] + (double)w - mid;
			Q[++tail] = v, mark[v] = 1;
		}
	}
}

IL bool Check(RG double mid, RG int u){
	RG int maxd = 0, flg = 0; t[0] = 0; 
	for(RG int e = first[u]; e != -1; e = edge[e].next){
		RG int v = edge[e].to, w = edge[e].w;
		if(vis[v]) continue;
		dis[v] = (double)w - mid, deep[v] = 1;
		Bfs(mid, v);
		for(RG int i = 0; i <= tail; ++i) mark[Q[i]] = 0;
		RG int he = 0, ta = -1, j = maxd;
		for(RG int i = 0; i <= tail; ++i){
			RG int x = Q[i];
			while(~j && j + deep[x] >= L){
				while(he <= ta && t[deq[ta]] < t[j]) --ta;
				deq[++ta] = j--;
			}
			while(he <= ta && deep[x] + deq[he] > U) ++he;
			if(he <= ta && dis[x] + t[deq[he]] >= 0){
				flg = 1;
				break;
			}
		}
		maxd = max(maxd, deep[Q[tail]]);
		if(flg) break;
		for(RG int i = 0; i <= tail; ++i){
			RG int x = Q[i];
			t[deep[x]] = max(t[deep[x]], dis[x]);
		}
	}
	for(RG int i = 0; i <= maxd; ++i) t[i] = -1e12;
	return flg;
}

IL void Calc(RG int u){
	RG double l = ans, r = tmp;
	while(r - l > EPS){
		RG double mid = (l + r) / 2;
		if(Check(mid, u)) l = mid;
		else r = mid;
	}
	ans = l;
}

IL void Solve(RG int u){
    vis[u] = 1, Calc(u);
    for(RG int e = first[u]; e != -1; e = edge[e].next){
        RG int v = edge[e].to;
        if(vis[v]) continue;
        root = 0, tot = size[v];
		if(tot <= L) continue;
        GetRoot(v, u), Solve(root);
    }
}

int main(RG int argc, RG char* argv[]){
    tot = n = Input(), L = Input(), U = Input();
	for(RG int i = 0; i <= n; ++i) first[i] = -1, t[i] = -1e12;
    for(RG int i = 1; i < n; ++i){
        RG int u = Input(), v = Input(), w = Input();
        Add(u, v, w), Add(v, u, w), tmp = max(tmp, w);
    }
	mx[0] = n + 1, GetRoot(1, 0), Solve(root);
    printf("%.3lf\n", ans);
    return 0;
}

posted @ 2018-02-25 21:47  Cyhlnj  阅读(121)  评论(0编辑  收藏  举报