有点类似NOI2014购票

  首先有方程$f(i)=min\{f(j)+(dep_i-dep_j)*p_i+q_i\}$

  这个显然是可以斜率优化的...

  $\frac {f(j)-f(k)}{dep_j-dep_k}<p_i$

  $p_i$是单调的,于是可以单调队列,当遍历完一个子树的时候,必须复原单调队列到进入这棵子树前的样子,这个用可持久化线段树维护可持久化数组显然可做...

  当然有更聪明的方法。

  单调队列队头出去的时候实际上队列信息不会被覆盖,于是恢复左端点只要记录进入当前点前的左端点即可。

  右端点可能会被覆盖,但是每次最多覆盖一个点(也就是当前点),于是恢复右端点只需要记录进入当前点前的右端点和被覆盖的值即可。

  但是这么做的话无法保证一个点出队入队次数是常数级别的,也就无法保证复杂度是$O(n)$了,所以每次不能一个一个出队(一条链加一朵大菊花就可以卡了),必须二分出队位置,才可以保证复杂度,做到$O(nlogn)$。

  但是出题人没有卡暴力出队的做法...我写二分比直接暴力弹出跑得快= =... 事实证明是评测机玄学...有时候跑得快有时候跑得慢

二分:

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#define ll long long
using namespace std;
const int maxn=, inf=1e9;
struct poi{int too, dis, pre;}e[maxn];
int n, x, z, tot, l, r;
int p[maxn], Q[maxn], last[maxn], nowl[maxn], nowr[maxn], nowqr[maxn], q[maxn];
ll f[maxn], d[maxn];
inline void read(int &k)
{
int f=; k=; char c=getchar();
while(c<'' || c>'') c=='-' && (f=-), c=getchar();
while(c<='' && c>='') k=k*+c-'', c=getchar();
k*=f;
}
inline void add(int x, int y, int z){e[++tot]=(poi){y, z, last[x]}; last[x]=tot;}
inline double xl(int j, int k){return 1.0*(f[j]-f[k])/(d[j]-d[k]);}
inline void dfs(int x)
{
nowl[x]=l;
if(l<r)
{
int L=l, R=r-;
while(L<R)
{
int mid=(L+R)>>;
if(p[x]-xl(q[mid+], q[mid])>1e-) L=mid+;
else R=mid;
}
l=(p[x]-xl(q[L], q[L+])>1e-)?L+:L;
}
nowr[x]=r;
if(x!=) f[x]=f[q[l]]+Q[x]+1ll*(d[x]-d[q[l]])*p[x];
if(l<r)
{
int L=l+, R=r;
while(L<R)
{
int mid=(L+R+)>>;
if(xl(x, q[mid])<xl(q[mid], q[mid-])) R=mid-;
else L=mid;
}
r=(xl(x, q[L])<xl(q[L-], q[L]))?L-:L;
}
nowqr[x]=q[r+]; q[++r]=x;
for(int i=last[x], too;i;i=e[i].pre) d[too=e[i].too]=d[x]+e[i].dis, dfs(too);
l=nowl[x]; q[r]=nowqr[x]; r=nowr[x];
}
int main()
{
read(n);
for(int i=;i<=n;i++) read(x), read(z), add(x, i, z), read(p[i]), read(Q[i]);
l=; r=; dfs();
for(int i=;i<=n;i++) printf("%lld\n", f[i]);
}

暴力出队:

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#define ll long long
using namespace std;
const int maxn=, inf=1e9;
struct poi{int too, dis, pre;}e[maxn];
int n, x, z, tot, l, r;
int p[maxn], Q[maxn], last[maxn], nowl[maxn], nowr[maxn], nowqr[maxn], q[maxn];
ll f[maxn], d[maxn];
inline void read(int &k)
{
int f=; k=; char c=getchar();
while(c<'' || c>'') c=='-' && (f=-), c=getchar();
while(c<='' && c>='') k=k*+c-'', c=getchar();
k*=f;
}
inline void add(int x, int y, int z){e[++tot]=(poi){y, z, last[x]}; last[x]=tot;}
inline double xl(int j, int k){return 1.0*(f[j]-f[k])/(d[j]-d[k]);}
inline void dfs(int x)
{
nowl[x]=l; while(l<r && p[x]-xl(q[l+], q[l])>1e-) l++;
if(x!=) f[x]=f[q[l]]+Q[x]+1ll*(d[x]-d[q[l]])*p[x];
nowr[x]=r; while(l<r && xl(x, q[r])<xl(q[r], q[r-])) r--;
nowqr[x]=q[r+]; q[++r]=x;
for(int i=last[x], too;i;i=e[i].pre) d[too=e[i].too]=d[x]+e[i].dis, dfs(too);
l=nowl[x]; q[r]=nowqr[x]; r=nowr[x];
}
int main()
{
read(n);
for(int i=;i<=n;i++) read(x), read(z), add(x, i, z), read(p[i]), read(Q[i]);
l=; r=; dfs();
for(int i=;i<=n;i++) printf("%lld\n", f[i]);
}

洛谷P3994 Highway(树形DP+斜率优化+可持久化线段树/二分)的更多相关文章

  1. P3994 高速公路 树形DP+斜率优化+二分

    $ \color{#0066ff}{ 题目描述 }$ C国拥有一张四通八达的高速公路网树,其中有n个城市,城市之间由一共n-1条高速公路连接.除了首都1号城市,每个城市都有一家本地的客运公司,可以发车 ...

  2. 洛谷P4027 [NOI2007]货币兑换(dp 斜率优化 cdq 二分)

    题意 题目链接 Sol 解题的关键是看到题目里的提示... 设\(f[i]\)表示到第\(i\)天所持有软妹币的最大数量,显然答案为\(max_{i = 1}^n f[i]\) 转移为\(f_i = ...

  3. bzoj3672: [Noi2014]购票(树形DP+斜率优化+可持久化凸包)

    这题的加强版,多了一个$l_i$的限制,少了一个$p_i$的单调性,难了好多... 首先有方程$f(i)=min\{f(j)+(dep_i-dep_j)*p_i+q_i\}$ $\frac {f(j) ...

  4. 洛谷P2365 任务安排(斜率优化dp)

    传送门 思路: 最朴素的dp式子很好考虑:设\(dp(i,j)\)表示前\(i\)个任务,共\(j\)批的最小代价. 那么转移方程就有: \[ dp(i,j)=min\{dp(k,j-1)+(sumT ...

  5. 洛谷P2120 [ZJOI2007]仓库建设 斜率优化DP

    做的第一道斜率优化\(DP\)QwQ 原题链接1/原题链接2 首先考虑\(O(n^2)\)的做法:设\(f[i]\)表示在\(i\)处建仓库的最小费用,则有转移方程: \(f[i]=min\{f[j] ...

  6. 洛谷4072 SDOI2016征途 (斜率优化+dp)

    首先根据题目中给的要求,推一下方差的柿子. \[v\times m^2 = m\times \sum x^2 - 2 \times sum \times sum +sum*sum \] 所以\(ans ...

  7. Codeforces 1179D 树形DP 斜率优化

    题意:给你一颗树,你可以在树上添加一条边,问添加一条边之后的简单路径最多有多少条?简单路径是指路径中的点只没有重复. 思路:添加一条边之后,树变成了基环树.容易发现,以基环上的点为根的子树的点中的简单 ...

  8. 洛谷P4072 [SDOI2016]征途(斜率优化)

    传送门 推式子(快哭了……)$$s^2*m^2=\sum _{i=1}^m (x_i-\bar{x})^2$$ $$s^2*m^2=m*\sum _{i=1}^m x_i^2-2*sum_n\sum ...

  9. 洛谷3571 POI2014 SUP-Supercomputer (斜率优化)

    一道神仙好题. 首先看到有多组\(k\),第一反应就是离线. 考虑贪心. 我们每次一定是尽量选择有儿子的节点.以便于我们下一次扩展. 但是对于一个\(k\),每次贪心的复杂度是\(O(n)\) 总复杂 ...

随机推荐

  1. linux运维升级路线

    运维工程师是从一个呆逼进化为苦逼再成长为牛逼的过程,前提在于你要能忍能干能拼,还要具有敏锐的嗅觉感知前方潮流变化.如:今年大数据,人工智能比较火……(相对表示就是 Python 比较火) 之前写过运维 ...

  2. AS的使用技巧

    title: AS的使用技巧 date: 2016-04-01 23:34:11 tags: [AndroidStudio] categories: [Tool,IDE] --- 概述 本文记录如何使 ...

  3. Fourteenth scrum meeting

    闫昊 今日完成:整理上一阶段代码,规划第二阶段实施过程 明日完成:学习讨论区开发 唐彬 今日完成:整理上一阶段代码,规划第二阶段实施过程 明日完成:学习学习进度部分开发 史烨轩 今日完成:整理上一阶段 ...

  4. Linux读书笔记第一、二章

    第一章    Linux内核简介 1.1Unix历史 Unix特点:1.很简洁 2.所有东西都被当成文件对待 3.Unix内核和相关的系统工具软件都是用C语言编写而成 4.进程创建非常迅速 1.2追寻 ...

  5. mvc 页面方法学习-RenderBody方法

    教程地址:https://docs.microsoft.com/zh-cn/aspnet/mvc/overview/getting-started/introduction/adding-a-view

  6. 第二章 Socket用法详解

    构造Socket Socket构造方法如下: Socket() //Creates an unconnected socket, with the system-default type of Soc ...

  7. Python开发【第五篇】迭代器、生成器、递归函数、二分法

    阅读目录 一.迭代器 1. 迭代的概念 #迭代器即迭代的工具(自定义的函数),那什么是迭代呢? #迭代:指一个重复的过程,每次重复都可以称之为一次迭代,并且每一次重复的结果是下一个迭代的初始值(例如: ...

  8. 第一个spring冲刺心得及感想

    在这次spring中,学到了不少东西: 1.团队协作精神 2.任务细节化,任务燃尽图 3.身为sm的责任 但是在过程中也认识到了一些不足 1.对于团队协作完成一个大的项目还是不熟悉 2.个人能力的不足 ...

  9. Linux内核0.11 setup文件说明

    一.总体功能介绍 这是关于Linux-kernel-0.11中boot文件夹下setup.s源文件的实现功能的总结说明. setup.s是一个操作系统加载程序,它的主要功能是利用BIOS中断读取机器系 ...

  10. ubuntu下安装软件的三种方法

    在ubuntu当中,安装应用程序常用的三种方法,分别是apt-get,dpkg安装deb和make install安装源码包三种. apt-get方法 使用apt-get install来安装应用程序 ...