题目描述

输入

第一行包含一个正整数N,表示X国的城市个数. 第二行包含两个正整数L和U,表示政策要求的第一期重建方案中修建道路数的上下限 接下来的N-1行描述重建小组的原有方案,每行三个正整数Ai,Bi,Vi分别表示道路(Ai,Bi),其价值为Vi 其中城市由1..N进行标号

输出

输出最大平均估值,保留三位小数

样例输入

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

样例输出

2.500

提示

N<=100000,1<=L<=U<=N-1,Vi<=1000000

这题算是长链剖分最经典的一道题了。

首先要找一个最优方案,直接DP显然不好做,那么考虑二分答案然后验证,因为是浮点数,要注意精度问题。

假设当前二分的答案是k,判断答案是否满足时原式也就转化成了$\frac{\sum_{ }^{ }vi}{|S|}>=k$,将分母移到不等式右边得到$\sum_{ }^{ }vi>=k*|S|$

将右边的部分移到左边就变成了$\sum_{ }^{ }vi-k*|S|>=0$

因为vi的个数就是|S|,因此将k*|S|放到Σ里面,判断就变成了$\sum_{ }^{ }(vi-k)>=0$

每次判断只要把每条边的边权和减k,再判断能否有一条路径边权和大于等于0就好了。

怎么判断呢?很容易想到O(n^2)dp,设f[i][j]表示i子树中与i距离为j的链的边权和最大值,枚举另一棵子树找到链长在[L-j,R-j]之内的边权最大值。

O(n^2)dp显然不行,但观察到dp是可合并的以深度为下标的转移方程,因此可以用长链剖分优化成O(n)。

怎么优化呢?

首先对整棵树长链剖分求出树剖序,再把树剖序架到线段树上,因为整棵树是由所有长链组成的,每条长链因为是优先遍历所以在树剖序上是连续的一段。

也就是说树剖序上的每一段都是树剖出的一条长链。那么每个点子树中每个深度的信息就可以都存到这个点往下的长链上。

当做树形DP时,每个点对于重儿子回溯时不做任何操作,直接继承;当轻儿子回溯时枚举轻儿子每个深度的边权和最大值,在长链上找到对应区间求最大值来更新答案。

然后再把这个轻儿子的信息合并到长链上。因为长链上存的是之前所有遍历过的子树合并后的信息,所以相当于每个点子树中有用的信息都在这个点往下的长链上。最后别忘了考虑从上到下的每条直链。

这样DP是O(n)的,再加上线段树的O(log)和二分的O(log)一共是O(nlog2n)。

至于这样DP为什么是O(n)的?

因为每个点对重儿子是直接继承的,而每个点需要被DP当且仅当它是轻儿子时,这时它一定是一个长链的链头,DP的时间复杂度是这个点往下的长链长度,那么DP的总复杂度就是每条长链的链长总和,也就是O(n)。

更加详细的有关长链剖分的讲解参见->长链剖分

  1. #include<set>
  2. #include<map>
  3. #include<cmath>
  4. #include<queue>
  5. #include<stack>
  6. #include<cstdio>
  7. #include<cstring>
  8. #include<iostream>
  9. #include<algorithm>
  10. using namespace std;
  11. int n;
  12. int L,R;
  13. int tot;
  14. int cnt;
  15. int x,y,z;
  16. double ans;
  17. int s[100010];
  18. int d[100010];
  19. int f[100010];
  20. int v[200010];
  21. int mn[100010];
  22. int to[200010];
  23. int son[100010];
  24. int num[100010];
  25. int head[100010];
  26. int next[200010];
  27. double mx[800010];
  28. double val[200010];
  29. double dis[100010];
  30. double dep[100010];
  31. double INF=1e15;
  32. double eps=1e-4;
  33. void add(int x,int y,int z)
  34. {
  35. tot++;
  36. next[tot]=head[x];
  37. head[x]=tot;
  38. to[tot]=y;
  39. v[tot]=z;
  40. }
  41. void dfs(int x)
  42. {
  43. d[x]=d[f[x]]+1;
  44. mn[x]=d[x];
  45. for(int i=head[x];i;i=next[i])
  46. {
  47. if(to[i]!=f[x])
  48. {
  49. f[to[i]]=x;
  50. dfs(to[i]);
  51. mn[x]=max(mn[x],mn[to[i]]);
  52. if(mn[to[i]]>mn[son[x]])
  53. {
  54. son[x]=to[i];
  55. }
  56. }
  57. }
  58. }
  59. void dfs2(int x)
  60. {
  61. s[x]=++cnt;
  62. if(son[x])
  63. {
  64. dfs2(son[x]);
  65. }
  66. for(int i=head[x];i;i=next[i])
  67. {
  68. if(to[i]!=f[x]&&to[i]!=son[x])
  69. {
  70. dfs2(to[i]);
  71. }
  72. }
  73. }
  74. void build(int rt,int l,int r)
  75. {
  76. mx[rt]=-INF;
  77. if(l==r)
  78. {
  79. num[l]=rt;
  80. return ;
  81. }
  82. int mid=(l+r)>>1;
  83. build(rt<<1,l,mid);
  84. build(rt<<1|1,mid+1,r);
  85. }
  86. void change(int rt,int l,int r,int k,double x)
  87. {
  88. if(l==r)
  89. {
  90. mx[rt]=max(mx[rt],x);
  91. return ;
  92. }
  93. int mid=(l+r)>>1;
  94. if(k<=mid)
  95. {
  96. change(rt<<1,l,mid,k,x);
  97. }
  98. else
  99. {
  100. change(rt<<1|1,mid+1,r,k,x);
  101. }
  102. mx[rt]=max(mx[rt<<1],mx[rt<<1|1]);
  103. }
  104. double query(int rt,int l,int r,int L,int R)
  105. {
  106. if(L>R)
  107. {
  108. return -INF;
  109. }
  110. if(L<=l&&r<=R)
  111. {
  112. return mx[rt];
  113. }
  114. int mid=(l+r)>>1;
  115. double res=-INF;
  116. if(L<=mid)
  117. {
  118. res=max(res,query(rt<<1,l,mid,L,R));
  119. }
  120. if(R>mid)
  121. {
  122. res=max(res,query(rt<<1|1,mid+1,r,L,R));
  123. }
  124. return res;
  125. }
  126. void tree_dp(int x)
  127. {
  128. change(1,1,n,s[x],dis[x]);
  129. for(int i=head[x];i;i=next[i])
  130. {
  131. if(son[x]==to[i])
  132. {
  133. dis[son[x]]=dis[x]+val[i];
  134. tree_dp(son[x]);
  135. }
  136. }
  137. for(int i=head[x];i;i=next[i])
  138. {
  139. if(to[i]!=f[x]&&to[i]!=son[x])
  140. {
  141. dis[to[i]]=dis[x]+val[i];
  142. tree_dp(to[i]);
  143. for(int j=1;j<=mn[to[i]]-d[x];j++)
  144. {
  145. dep[j]=mx[num[s[to[i]]+j-1]];
  146. if(j<=R)
  147. {
  148. ans=max(ans,query(1,1,n,max(s[x],s[x]+L-j),min(s[x]+mn[x]-d[x],s[x]+R-j))+dep[j]-2*dis[x]);
  149. }
  150. }
  151. for(int j=1;j<=mn[to[i]]-d[x];j++)
  152. {
  153. change(1,1,n,s[x]+j,dep[j]);
  154. }
  155. }
  156. }
  157. ans=max(ans,query(1,1,n,s[x]+L,min(s[x]+mn[x]-d[x],s[x]+R))-dis[x]);
  158. }
  159. int main()
  160. {
  161. scanf("%d%d%d",&n,&L,&R);
  162. for(int i=1;i<n;i++)
  163. {
  164. scanf("%d%d%d",&x,&y,&z);
  165. add(x,y,z);
  166. add(y,x,z);
  167. }
  168. dfs(1);
  169. dfs2(1);
  170. double l=0;
  171. double r=1000000;
  172. while(r-l>eps)
  173. {
  174. double mid=(l+r)/2;
  175. for(int i=1;i<=tot;i++)
  176. {
  177. val[i]=v[i]-mid;
  178. }
  179. ans=-INF;
  180. dis[1]=0;
  181. build(1,1,n);
  182. tree_dp(1);
  183. if(ans<0)
  184. {
  185. r=mid;
  186. }
  187. else
  188. {
  189. l=mid;
  190. }
  191. }
  192. printf("%.3lf",l);
  193. }

BZOJ1758[Wc2010]重建计划——分数规划+长链剖分+线段树+二分答案+树形DP的更多相关文章

  1. 2019.01.21 bzoj1758: [Wc2010]重建计划(01分数规划+长链剖分+线段树)

    传送门 长链剖分好题. 题意简述:给一棵树,问边数在[L,R][L,R][L,R]之间的路径权值和与边数之比的最大值. 思路: 用脚指头想都知道要01分数规划. 考虑怎么checkcheckcheck ...

  2. BZOJ.1758.[WC2010]重建计划(分数规划 点分治 单调队列/长链剖分 线段树)

    题目链接 BZOJ 洛谷 点分治 单调队列: 二分答案,然后判断是否存在一条长度在\([L,R]\)的路径满足权值和非负.可以点分治. 对于(距当前根节点)深度为\(d\)的一条路径,可以用其它子树深 ...

  3. BZOJ 1758 / Luogu P4292 [WC2010]重建计划 (分数规划(二分/迭代) + 长链剖分/点分治)

    题意 自己看. 分析 求这个平均值的最大值就是分数规划,二分一下就变成了求一条长度在[L,R]内路径的权值和最大.有淀粉质的做法但是我没写,感觉常数会很大.这道题可以用长链剖分做. 先对树长链剖分. ...

  4. [WC2010]重建计划(长链剖分+线段树+分数规划)

    看到平均值一眼分数规划,二分答案mid,边权变为w[i]-mid,看是否有长度在[L,R]的正权路径.设f[i][j]表示以i为根向下j步最长路径,用长链剖分可以优化到O(1),查询答案线段树即可,复 ...

  5. 洛谷 P4292 - [WC2010]重建计划(长链剖分+线段树)

    题面传送门 我!竟!然!独!立!A!C!了!这!道!题!incredible! 首先看到这类最大化某个分式的题目,可以套路地想到分数规划,考虑二分答案 \(mid\) 并检验是否存在合法的 \(S\) ...

  6. bzoj 1758 [Wc2010]重建计划 分数规划+树分治单调队列check

    [Wc2010]重建计划 Time Limit: 40 Sec  Memory Limit: 162 MBSubmit: 4345  Solved: 1054[Submit][Status][Disc ...

  7. 2019.03.11 COGS2652 秘术(天文密葬法)(分数规划+长链剖分)

    传送门 题意:nnn个点的树,每个点两个值a,ba,ba,b,问长度为mmm的路径∑ai∑bi\frac{\sum a_i}{\sum b_i}∑bi​∑ai​​的最大值. 思路:一眼要01分数规划, ...

  8. 2018牛客网暑假ACM多校训练赛(第七场)I Tree Subset Diameter 动态规划 长链剖分 线段树

    原文链接https://www.cnblogs.com/zhouzhendong/p/NowCoder-2018-Summer-Round7-I.html 题目传送门 -  https://www.n ...

  9. BZOJ.3653.谈笑风生(长链剖分/线段树合并/树状数组)

    BZOJ 洛谷 \(Description\) 给定一棵树,每次询问给定\(p,k\),求满足\(p,a\)都是\(b\)的祖先,且\(p,a\)距离不超过\(k\)的三元组\(p,a,b\)个数. ...

随机推荐

  1. Just oj 2018 C语言程序设计竞赛(高级组)H: CBT?

    H: CBT? 时间限制: 1 s      内存限制: 128 MB      提交 我的状态 题目描述 对于二叉树,如果这棵树的节点排布是按行从上到下,每行从左到右挨个放置,中间不会有空闲的节点. ...

  2. 51Nod 1265 四点共面(计算几何)

    1265 四点共面  基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题 给出三维空间上的四个点(点与点的位置均不相同),判断这4个点是否在同一个平面内(4点共线也算共面). ...

  3. Senparc.Weixin微信开发(1) 开发验证

    官方系列教程 http://www.cnblogs.com/szw/archive/2013/05/20/3089479.html 登录微信公众平台后-左侧找到开发--启用服务器配置 这样,我们才可以 ...

  4. PEM routines:PEM_read_bio:no start line

    https://blog.csdn.net/xiejunna/article/details/71151006 在放置证书后,运行nodejs抛异常:PEM routines:PEM_read_bio ...

  5. rabbitmq3.7.5 centos7 安装笔记

    先安装各种依赖文件: yum -y install gcc glibc-devel make ncurses-devel openssl-devel xmlto perl wget vim 1. ra ...

  6. 【BZOJ1778】[Usaco2010 Hol]Dotp 驱逐猪猡

    题解: 网上有一种复杂的方法..好像复杂度并没有优势就没看 定义f[i]表示i的期望经过次数,f[i]=sigma{f[j]*p/q/du[j]}+(i==1); 然后高斯消元就可以了 最后求出来的f ...

  7. [转]Ubuntu默认使用root用户登录并免去输入密码

    启用Root用户登录 Ctrl + Alt + T进入终端, 输入cd /usr/share/lightm/ightm.conf.d, 如果提示你没有那个文件或目录.那就一次次的进入目录. 进入之后会 ...

  8. python全栈开发day55-mysql外键的三种变种

    一.昨日内容回顾 二.今日内容总结 三.mysql应知必会 你可以通过INFORMATION_SCHEMA.KEY_COLUMN_USAGE 表来查看. select * from INFORMATI ...

  9. python各个包的用途

    python中的多个包的用途 1.Numpy Numpy提供了两种基本的对象:ndarray和ufunc.ndarray是存储单一数据类型的多维数组,而ufunc是能够对数组进行处理的函数. N维数组 ...

  10. log4j快速入门

    转自:http://blog.csdn.net/yanwushu/article/details/7581255 1.引言 在应用程序中添加日志记录总的来说基于三个目的: .监视代码中变量的变化情况, ...