BZOJ 传送门

洛谷传送门

题目描述

X国遭受了地震的重创, 导致全国的交通近乎瘫痪,重建家园的计划迫在眉睫。X国由NNN个城市组成, 重建小组提出,仅需建立N−1" role="presentation" style="position: relative;">N−1N−1N-1条道路即可使得任意两个城市互相可达。于是,重建小组很快提出了一个包含N−1N−1N-1条道路的方案,并满足城市之间两两可达,他们还计算评估了每条道路eee建设之后可以带来的价值v(e)" role="presentation" style="position: relative;">v(e)v(e)v(e)。
由于重建计划复杂而艰难,经费也有一定限制。因此,政府要求第一期重建工程修建的道路数目为kkk条,但需满足L≤k≤U" role="presentation" style="position: relative;">L≤k≤UL≤k≤UL ≤ k ≤ U, 即不应少于LLL条,但不超过U" role="presentation" style="position: relative;">UUU条。同时,为了最大化利用率,要求建设的这些道路恰好组成一条简单路径,即所建设的kkk条路径可以构成一个排列e1=(p1,q1),e2=(p2,q2),...ek=(pk,qk)" role="presentation" style="position: relative;">e1=(p1,q1),e2=(p2,q2),...ek=(pk,qk)e1=(p1,q1),e2=(p2,q2),...ek=(pk,qk)e_1 = (p_1, q_1), e_2 = (p_2, q_2), ... e_k = (p_k, q_k), 对于 1≤i<k1≤i<k1 ≤ i , 有(qi=pi+1qi=pi+1q_i = p_i+1)。

重建小组打算修改他们的原有方案以满足要求,即在原有的N−1N−1N-1条道路中寻找一条路径SSS作为新的方案,使得新方案中的道路平均价值

AvgValue=∑e∈Sv(e)|S|" role="presentation">AvgValue=∑e∈Sv(e)|S|AvgValue=∑e∈Sv(e)|S|

AvgValue=\frac{\sum_{e \in S}v(e)}{|S|}
最大。这里v(e)v(e)v(e)表示道路eee的价值,|S|" role="presentation" style="position: relative;">|S||S||S|表示新方案中道路的条数。请你帮助重建小组寻找一个最优方案。 注: 在本题中LLL和U" role="presentation" style="position: relative;">UUU的设置将保证有解。

输入输出格式

输入格式:

第一行包含一个正整数NNN,表示X国的城市个数。
第二行包含两个正整数L" role="presentation" style="position: relative;">LLL、UUU,表示政府要求的第一期重建方案中修建道路数的上下限。
接下来的N−1" role="presentation" style="position: relative;">N−1N−1N-1行描述重建小组的原有方案,每行三个正整数ai,bi,viai,bi,via_i, b_i, v_i,分别表示道路(ai,bi)(ai,bi)(a_i, b_i),其价值为viviv_i。其中城市由1…N1…N1…N标号。

输出格式:

仅包含一行,为一个实数AvgValueAvgValueAvgValue,即最大平均价值。
小数点后保留三位。

输入输出样例

输入样例#1:

4
2 3
1 2 1
1 3 2
1 4 3

输出样例#1:

2.500

说明

新方案中选择路径(3,1)(3,1)(3, 1), (1,4)(1,4)(1, 4)可以得到的平均价值为2.52.52.5,为最大平均价值。
对于20%20%20\%的数据,N≤5000N≤5000N ≤ 5 000;
另有30%30%30\%的数据,N≤100000N≤100000N ≤ 100 000, 原有方案恰好为一条路径(链);
对于100%100%100\%的数据,N≤100000,1≤L≤U≤N−1,vi≤106N≤100000,1≤L≤U≤N−1,vi≤106N ≤ 100 000, 1 ≤ L ≤ U ≤ N-1, v_i ≤ 10^6。

解题分析

保留三位小数和4s的时限这些条件可以看出这道题是二分答案+点分树, 然后博主就不会做了…
搜了搜题解, 发现此题是一道分数规划问题。
最简单无脑不需二分的想法是直接做一遍点分, 同时对经过分治重心的边暴力更新。但这样搞是每一层O(N2)O(N2)O(N^2),总复杂度达到了O(N2log2(N))O(N2log2(N))O(N^2log^2(N)) 显然不可过。
在这道题中, 我们想要求得一条路径使得其平均值最大, 那么可以二分这个平均值AveValveAveValveAveValve, 将所有边的边权减去这个二分得到的值。如果一条路径长度位于[L,U][L,U][L,U]之间且边权和大于0, 则说明这个值是可行的。
现在我们有一个O(Nlog3(N))O(Nlog3(N))O(Nlog^3(N))的做法。 对于每个分治重心, 我们考虑用线段树维护到分治重心距离为xxx的点的边权前缀和最大值。 对于每一个在子树上的点D" role="presentation" style="position: relative;">DDD,设其深度为depdepdep , 则在线段树上对应一段区间为[L−dep,U−dep][L−dep,U−dep][L-dep, U-dep], 我们只需要查区间最大值即可。
但经网上大神们实验, 这个复杂度也是过不去的… 我们需要一个O(N)O(N)O(N)更新一层分治重心的算法。
上面我们说到了, 每一个子树上的点有唯一对应的一段查询区间, 并且这个区间随子树上点深度的增大而向更小方向移动。这样具有单调性的查询可以用单调队列来优化至(O(N))(O(N))(O(N))。
还有很多卡常的小细节, 其实做到了常数并不大, 详见代码。

#include <cstdio>
#include <algorithm>
#include <cctype>
#include <cstdlib>
#include <cstring>
#include <cmath>
#define R register
#define W while
#define gc getchar()
#define db double
#define IN inline
#define EPS 1e-4
#define INF 1e12
#define MX 200005
bool neg;
template <class T>
IN void in(T &x)
{x = 0; R char c = gc;W (!isdigit(c)) {if(c == '-') neg = true; c = gc;}W (isdigit(c))x = (x << 1) + (x << 3) + c - 48, c = gc;if(neg) neg = false, x = -x;
}
db ans, len[MX], rec[MX];
int dot, D, U, cnt, deal, root, ub, dpmx, cot;
int dep[MX], fat[MX], siz[MX], mx[MX], que[MX],
sub[MX], ds[MX], q[MX], head[MX], far[MX];
bool vis[MX];
IN bool cmp(const int &x, const int &y)
{return far[x] < far[y];}
struct Edge
{int to, len, nex;
}edge[MX << 1];
IN void addedge(const int &from, const int &to, const int &len)
{edge[++cnt] = {to, len, head[from]};head[from] = cnt;
}
namespace FP
{IN bool check(const int &cent, const db &cut){R int lb, rb, hd, tl, now, dl;dpmx = 0;for (R int t = 1; t <= cot; ++t){if(vis[sub[t]]) continue;que[hd = tl = 0] = sub[t];dep[sub[t]] = 1;len[sub[t]] = ds[sub[t]] - cut;fat[sub[t]] = cent;W (hd <= tl){now = que[hd++];for (R int i = head[now]; i; i = edge[i].nex)//BFS一遍, 在队列中点深度不降{if(edge[i].to == fat[now] || vis[edge[i].to]) continue;que[++tl] = edge[i].to, fat[edge[i].to] = now,dep[edge[i].to] = dep[now] + 1, len[edge[i].to] = edge[i].len + len[now] - cut;}}lb = 1, rb = 0, dl = dpmx;//dpmx指处理的所有子树中最深的深度for (R int i = 0; i <= tl; ++i){now = que[i];W (dl >= 0 && dl + dep[now] >= D)//更新能够加入队列的最大值{W (lb <= rb && rec[q[rb]] <= rec[dl]) --rb;q[++rb] = dl, --dl;}W (lb <= rb && dep[now] + q[lb] > U) ++lb;//处理超过上限的部分if(lb <= rb && len[now] + rec[q[lb]] >= EPS) return true;}for (R int i = dpmx + 1; i <= dep[que[tl]]; ++i) rec[i] = -INF;//多出的部分赋为新值for (R int i = 0; i <= tl; ++i) rec[dep[que[i]]] = std::max(rec[dep[que[i]]], len[que[i]]);//更新最优答案dpmx = std::max(dpmx, dep[que[tl]]);}return false;}
}
namespace Dtdv
{void DFS(const int &now, const int &fa, const int &fr){//far 存子树最大深度, 方便分数规划时对子树排序siz[now] = 1; far[now] = fr;for (R int i = head[now]; i; i = edge[i].nex){if(vis[edge[i].to] || edge[i].to == fa) continue;DFS(edge[i].to, now, fr + 1);siz[now] += siz[edge[i].to];far[now] = std::max(far[now], far[edge[i].to]);}}void getroot(const int &now, const int &fa){mx[now] = 0;for (R int i = head[now]; i; i = edge[i].nex){if(vis[edge[i].to] || edge[i].to == fa) continue;getroot(edge[i].to, now);mx[now] = std::max(mx[now], siz[edge[i].to]);}mx[now] = std::max(mx[now], deal - siz[now]);if(mx[now] < mx[root]) root = now;}void solve(R int now){DFS(now, 0, 0); cot = 0;for (R int i = head[now]; i; i = edge[i].nex){if(vis[edge[i].to]) continue;sub[++cot] = edge[i].to, ds[edge[i].to] = edge[i].len;//ds存分治重心到子树上第一个节点的距离}std::sort(sub + 1, sub + 1 + cot, cmp);db dn = ans, up = ub, mid;W (233)//二分确定答案{if(up - dn < EPS) break;mid = (up + dn) / 2.0;if(FP::check(now, mid)) dn = mid; else up = mid;}ans = dn; vis[now] = true;for (R int i = head[now]; i; i = edge[i].nex){if(vis[edge[i].to]) continue;DFS(edge[i].to, 0, 0);mx[root = 0] = deal = siz[edge[i].to];if(siz[edge[i].to] < D) continue;getroot(edge[i].to, 0);solve(root);}}
}
int main(void)
{int a, b, c;in(dot), in(D), in(U);for (R int i = 1; i < dot; ++i) in(a), in(b), in(c), addedge(a, b, c), addedge(b, a, c), ub = std::max(ub, c);//提前确定二分上界, 减少二分次数Dtdv::DFS(1, 0, 0);mx[root = 0] = deal = siz[1];Dtdv::getroot(1, 0);Dtdv::solve(root);printf("%.3lf", ans);
}

[Luogu P4292] [BZOJ 1758] [WC2010]重建计划相关推荐

  1. [luogu 4292][bzoj 1758][WC2010] 重建计划(点分治 + dp + 单调队列优化 + 启发式合并)

    [WC2010]重建计划 problem solution code problem 洛谷指路 solution 一看那个道路平均价值的式子:AvgValue=∑e∈Sv(e)∣S∣\text{Avg ...

  2. 1758: [Wc2010]重建计划(TLE)

    链接 http://www.lydsy.com/JudgeOnline/problem.php?id=1758 题解? 首先说明这道题我没过. 那为啥要写题解? 因为我确实写的正解啊. 不就是先二分答 ...

  3. 【bzoj1758】[Wc2010]重建计划

    Description Input 第一行包含一个正整数N,表示X国的城市个数. 第二行包含两个正整数L和U,表示政策要求的第一期重建方案中修建道路数的上下限 接下来的N-1行描述重建小组的原有方案, ...

  4. bzoj1758 [Wc2010]重建计划

    http://www.elijahqi.win/2018/01/20/bzoj1758/ Description Input 第一行包含一个正整数N,表示X国的城市个数. 第二行包含两个正整数L和U, ...

  5. 点分治问题 ----------- luoguP2942 [WC2010]重建计划 [点分治 + bfs + 单调队列 + 预处理建树 + 二分 + 01分数规划]

    题目链接 解题思路: 1.对于这个Avgvalue=∑e∈sv(e)∣s∣Avgvalue = \frac{\sum_{e\in s}v(e)}{|s|}Avgvalue=∣s∣∑e∈s​v(e)​ ...

  6. Bzoj1758: [Wc2010]重建计划

    题面 传送门 Sol 题意就是给你一棵树,有边权 求边数在[L,U][L,U][L, U]内的一条路径,使得边权和除以边数最大,输出这个最大值 二分答案+点分治+单调队列 二分一个答案midmidmi ...

  7. BZOJ 2957楼房重建

    传送门 线段树 //Twenty #include<cstdio> #include<cstdlib> #include<iostream> #include< ...

  8. Luogu P1198 BZOJ 1012 最大数 (线段树)

    Luogu P1198 BZOJ 1012 最大数 (线段树) 手动博客搬家: 本文发表于20170821 14:32:05, 原地址https://blog.csdn.net/suncongbo/a ...

  9. BZOJ 5424: 烧桥计划

    BZOJ 5424: 烧桥计划 目前暂居rk1QAQ 首先,设\(f[i][k]\)为前i个点中,选了第i个点,总共选了k个点的答案.那么就有: \[f[i][k]=min_{j<i}\{f[j ...

最新文章

  1. 建设有竞争力的APP开发团队
  2. json_encode 中文乱码
  3. Leetcode 124题:求⼆叉树中最⼤路径和
  4. vue inheritAttrs、$attrs和$listeners使用
  5. 前端面试技巧和注意事项_前端面试百分之九十九过的技巧
  6. 【从入门到放弃-ZooKeeper】ZooKeeper实战-分布式队列
  7. Err CLSU-00104: additional error information: need ha priv
  8. 脚本语言、编程语言区别与联系
  9. Git版本控制及Goland使用Git教程
  10. 计算机集成声卡输出通道,电脑集成与独立声卡的差别有哪些?
  11. 《QTreeView表项实现排序的2种方式》:系列教程之九
  12. 修改配色 Mac电脑默认命令行工具(终端\terminal) MacBook Pro MacBook
  13. bigWigToBedGraph格式转换
  14. mysql写了代码如何看表_mysql之单表查询__我自己敲的代码
  15. MS Castap学习(1)
  16. OpenNURBS 3DM Viewer
  17. 矩阵分解(java)
  18. 用 Python 开发简单交互式 Web 应用
  19. python 有损和无损 方式替换表情符号
  20. Access2003中文版应用基础教程(奋斗的小鸟)_PDF 电子书

热门文章

  1. YTU----1607: 字符棱形
  2. ACM2019春季训练- How Many Tables HDU - 1213(初识并查集+转倚天屠龙记故事)
  3. 如何利用计算机完成一篇文稿制作,怎么制作美篇-教你打造一篇原创内容撰写步骤...
  4. H5网页头部的声明应该是用 lang=zh 还是 lang=zh-cn?
  5. 小程序如何实现定点跳转其他的小程序(京东、苏宁)的具体店铺或商品页面
  6. DW卷积、PW卷积、转置卷积、膨胀卷积(空洞卷积)、可变形卷积一次看个够
  7. Android软件开发实例:用客户端写博客
  8. CSS样式书写顺序 与 浏览器内部加载原理
  9. el-table自定义排序
  10. 南京长江隧道发生连环追尾事故 没有人员伤亡-南京-隧道-追尾