BZOJ1758[Wc2010]重建计划——分数规划+长链剖分+线段树+二分答案+树形DP
题目描述

输入
第一行包含一个正整数N,表示X国的城市个数. 第二行包含两个正整数L和U,表示政策要求的第一期重建方案中修建道路数的上下限 接下来的N-1行描述重建小组的原有方案,每行三个正整数Ai,Bi,Vi分别表示道路(Ai,Bi),其价值为Vi 其中城市由1..N进行标号
输出
输出最大平均估值,保留三位小数
样例输入
2 3
1 2 1
1 3 2
1 4 3
样例输出
提示
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)。
更加详细的有关长链剖分的讲解参见->长链剖分
#include<set>
#include<map>
#include<cmath>
#include<queue>
#include<stack>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int n;
int L,R;
int tot;
int cnt;
int x,y,z;
double ans;
int s[100010];
int d[100010];
int f[100010];
int v[200010];
int mn[100010];
int to[200010];
int son[100010];
int num[100010];
int head[100010];
int next[200010];
double mx[800010];
double val[200010];
double dis[100010];
double dep[100010];
double INF=1e15;
double eps=1e-4;
void add(int x,int y,int z)
{
tot++;
next[tot]=head[x];
head[x]=tot;
to[tot]=y;
v[tot]=z;
}
void dfs(int x)
{
d[x]=d[f[x]]+1;
mn[x]=d[x];
for(int i=head[x];i;i=next[i])
{
if(to[i]!=f[x])
{
f[to[i]]=x;
dfs(to[i]);
mn[x]=max(mn[x],mn[to[i]]);
if(mn[to[i]]>mn[son[x]])
{
son[x]=to[i];
}
}
}
}
void dfs2(int x)
{
s[x]=++cnt;
if(son[x])
{
dfs2(son[x]);
}
for(int i=head[x];i;i=next[i])
{
if(to[i]!=f[x]&&to[i]!=son[x])
{
dfs2(to[i]);
}
}
}
void build(int rt,int l,int r)
{
mx[rt]=-INF;
if(l==r)
{
num[l]=rt;
return ;
}
int mid=(l+r)>>1;
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
}
void change(int rt,int l,int r,int k,double x)
{
if(l==r)
{
mx[rt]=max(mx[rt],x);
return ;
}
int mid=(l+r)>>1;
if(k<=mid)
{
change(rt<<1,l,mid,k,x);
}
else
{
change(rt<<1|1,mid+1,r,k,x);
}
mx[rt]=max(mx[rt<<1],mx[rt<<1|1]);
}
double query(int rt,int l,int r,int L,int R)
{
if(L>R)
{
return -INF;
}
if(L<=l&&r<=R)
{
return mx[rt];
}
int mid=(l+r)>>1;
double res=-INF;
if(L<=mid)
{
res=max(res,query(rt<<1,l,mid,L,R));
}
if(R>mid)
{
res=max(res,query(rt<<1|1,mid+1,r,L,R));
}
return res;
}
void tree_dp(int x)
{
change(1,1,n,s[x],dis[x]);
for(int i=head[x];i;i=next[i])
{
if(son[x]==to[i])
{
dis[son[x]]=dis[x]+val[i];
tree_dp(son[x]);
}
}
for(int i=head[x];i;i=next[i])
{
if(to[i]!=f[x]&&to[i]!=son[x])
{
dis[to[i]]=dis[x]+val[i];
tree_dp(to[i]);
for(int j=1;j<=mn[to[i]]-d[x];j++)
{
dep[j]=mx[num[s[to[i]]+j-1]];
if(j<=R)
{
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]);
}
}
for(int j=1;j<=mn[to[i]]-d[x];j++)
{
change(1,1,n,s[x]+j,dep[j]);
}
}
}
ans=max(ans,query(1,1,n,s[x]+L,min(s[x]+mn[x]-d[x],s[x]+R))-dis[x]);
}
int main()
{
scanf("%d%d%d",&n,&L,&R);
for(int i=1;i<n;i++)
{
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
add(y,x,z);
}
dfs(1);
dfs2(1);
double l=0;
double r=1000000;
while(r-l>eps)
{
double mid=(l+r)/2;
for(int i=1;i<=tot;i++)
{
val[i]=v[i]-mid;
}
ans=-INF;
dis[1]=0;
build(1,1,n);
tree_dp(1);
if(ans<0)
{
r=mid;
}
else
{
l=mid;
}
}
printf("%.3lf",l);
}
BZOJ1758[Wc2010]重建计划——分数规划+长链剖分+线段树+二分答案+树形DP的更多相关文章
- 2019.01.21 bzoj1758: [Wc2010]重建计划(01分数规划+长链剖分+线段树)
传送门 长链剖分好题. 题意简述:给一棵树,问边数在[L,R][L,R][L,R]之间的路径权值和与边数之比的最大值. 思路: 用脚指头想都知道要01分数规划. 考虑怎么checkcheckcheck ...
- BZOJ.1758.[WC2010]重建计划(分数规划 点分治 单调队列/长链剖分 线段树)
题目链接 BZOJ 洛谷 点分治 单调队列: 二分答案,然后判断是否存在一条长度在\([L,R]\)的路径满足权值和非负.可以点分治. 对于(距当前根节点)深度为\(d\)的一条路径,可以用其它子树深 ...
- BZOJ 1758 / Luogu P4292 [WC2010]重建计划 (分数规划(二分/迭代) + 长链剖分/点分治)
题意 自己看. 分析 求这个平均值的最大值就是分数规划,二分一下就变成了求一条长度在[L,R]内路径的权值和最大.有淀粉质的做法但是我没写,感觉常数会很大.这道题可以用长链剖分做. 先对树长链剖分. ...
- [WC2010]重建计划(长链剖分+线段树+分数规划)
看到平均值一眼分数规划,二分答案mid,边权变为w[i]-mid,看是否有长度在[L,R]的正权路径.设f[i][j]表示以i为根向下j步最长路径,用长链剖分可以优化到O(1),查询答案线段树即可,复 ...
- 洛谷 P4292 - [WC2010]重建计划(长链剖分+线段树)
题面传送门 我!竟!然!独!立!A!C!了!这!道!题!incredible! 首先看到这类最大化某个分式的题目,可以套路地想到分数规划,考虑二分答案 \(mid\) 并检验是否存在合法的 \(S\) ...
- bzoj 1758 [Wc2010]重建计划 分数规划+树分治单调队列check
[Wc2010]重建计划 Time Limit: 40 Sec Memory Limit: 162 MBSubmit: 4345 Solved: 1054[Submit][Status][Disc ...
- 2019.03.11 COGS2652 秘术(天文密葬法)(分数规划+长链剖分)
传送门 题意:nnn个点的树,每个点两个值a,ba,ba,b,问长度为mmm的路径∑ai∑bi\frac{\sum a_i}{\sum b_i}∑bi∑ai的最大值. 思路:一眼要01分数规划, ...
- 2018牛客网暑假ACM多校训练赛(第七场)I Tree Subset Diameter 动态规划 长链剖分 线段树
原文链接https://www.cnblogs.com/zhouzhendong/p/NowCoder-2018-Summer-Round7-I.html 题目传送门 - https://www.n ...
- BZOJ.3653.谈笑风生(长链剖分/线段树合并/树状数组)
BZOJ 洛谷 \(Description\) 给定一棵树,每次询问给定\(p,k\),求满足\(p,a\)都是\(b\)的祖先,且\(p,a\)距离不超过\(k\)的三元组\(p,a,b\)个数. ...
随机推荐
- STL容器之优先队列
STL容器之优先队列 优先级队列,以前刷题的时候用的比较熟,现在竟然我只能记得它的关键字是priority_queue(太伤了).在一些定义了权重的地方这个数据结构是很有用的. 先回顾队列的定义:队列 ...
- 一脸懵逼学习Hadoop中的序列化机制——流量求和统计MapReduce的程序开发案例——流量求和统计排序
一:序列化概念 序列化(Serialization)是指把结构化对象转化为字节流.反序列化(Deserialization)是序列化的逆过程.即把字节流转回结构化对象.Java序列化(java.io. ...
- VMware安装操作系统提示 " Intel VT-x 处于禁用状态"解决方法
VMWARE WORKSTATION 在安装64为操作系统(kali)报错,报错内容为:“已将该虚拟机配置为使用 64 位客户机操作系统.但是,无法执行 64 位操作. 此主机支持 Intel VT- ...
- tomcat 反代配置
tomcat反代可以基于nginx , http进行反代 反代服务器: 有两个网口 反代服务器一般有两块网卡一块处于外网,一块处于内网用于与后端服务器通信 tomcat 节点处于内网地址 1 tom ...
- 51Nod1038 X^A Mod P 数论 原根 BSGS
原文链接https://www.cnblogs.com/zhouzhendong/p/51Nod1038.html 题目传送门 - 51Nod1038 题意 题解 在模质数意义下,求高次剩余,模板题. ...
- .net core webapi 将localhost改成ip地址
用管理员身份运行vs 添加引用 using Microsoft.AspNetCore.Cors; 修改 public void Configure(IApplicationBuilder app, I ...
- web发展阶段简介
web1.0.web2.0和web3.0的区别前言: 其实并没有什么所谓的2.0.3.0,因为你没法准确的界定它是什么样的应用,也没法界定它是什么时候开始的,什么时候结束,它只是互联网本身发展的一种 ...
- excel表格导入数据库数据存在则更新不存在添加
public void excelToDB() throws ParseException { String datapath = this.getParameter("datapath&q ...
- TensorFlow激活函数+归一化-函数
激活函数的作用如下-引用<TensorFlow实践>: 这些函数与其他层的输出联合使用可以生成特征图.他们用于对某些运算的结果进行平滑或者微分.其目标是为神经网络引入非线性.曲线能够刻画出 ...
- POJ 1321-棋盘问题【DFS+递归】
题目链接 题目大意: Description 在一个给定形状的棋盘(形状可能是不规则的)上面摆放棋子,棋子没有区别.要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列,请编程求解对于给定形状和大 ...