题目传送门

题目大意

有一个 \(n\) 个点的树,每个点有三个值 \(p_u,q_u,l_u\) ,现在可以从 \(u\) 走到点 \(v\) 当且仅当 \(v\) 是 \(u\) 的祖先并且 \(\text{dis}(u,v)\le l_u\) ,这样的花费为 \(\text{dis}(u,v)\times p_u+q_u\) 。问每个点到 \(1\) 所需的最小总花费。

\(n\le 2\times 10^5\) ,保证答案在 \(\text{long long}\) 范围内。

思路

还说还是看到 \(\text{Qiuly}\) 做这道题才做的,想要练习一下自己本来就菜的一批的斜率优化,结果发现自己除了斜率优化啥也不会了。。。

我们假设 \(f_u\) 为点 \(u\) 的答案,可以得到转移式:

\[f_u=\min_{v} \{f_v+(\text{deep}_u-\text{deep}_v )\times p_u+q_u\},s.t. \ \ v\in fa_u\wedge \text{deep}_u-\text{deep}_v\le l_u
\]
\[\Rightarrow f_u=q_u+\text{deep}_u\times p_u+\min_{v}\{f_v-\text{deep}_v\times p_u\}
\]

然后我们就发现这个式子可以斜率优化了。假设对于点 \(u\) 存在点 \(j\) 比点 \(k\) 更优,可以得到:

\[f_j-\text{deep}_j\times p_i\le f_k-\text{deep}_k\times p_i
\]
\[\Rightarrow p_i\ge \frac{f_j-f_k}{\text{deep}_j-\text{deep}_k}
\]

然后我们发现这个东西我们可以维护一个下凸壳,但是因为 \(p_i\) 并不单调,所以我们直接在凸壳上面二分找到第一个斜率不大于 \(p_i\) 的点就好了。


但是我们发现我们这个东西其实是一棵树,我们显然没办法直接套这个做法。我们先考虑在区间上的做法,再考虑拓展到树上。

我们发现其实我们可以 \(\text{cdq}\) 分治解决这个问题,即每次先递归解决左区间,然后在左区间的凸壳上考虑对于右区间的贡献,然后继续递归解决右区间。可以发现这样做的时间复杂度为 \(\Theta(n\log^2 n)\) 的。

考虑拓展到树上。我们发现其实我们可以用淀粉质解决这个问题,每次我们找到当前子树的重心,假设设为 \(x\) ,我们先递归解决该子树除了 \(x\) 的子树的部分(下面设为 \(S_1\)),那么我们可以考虑 \(S_1\) 对 \(x\) 的子树(下面设为 \(S_2\))产生的贡献,同上文,然后继续递归解决 \(S_2\)。

考虑分析时间复杂度,可以想到每个点的均摊时间复杂度就是点分树上的深度乘上对于一个点更新操作的时间,即为 \(\Theta(\log^2n)\) ,所以总时间复杂度即为 \(\Theta(n\log^2 n)\) 。


有几个细节需要提醒一下,就是说找重心的时候要找最接近于当前子树的根的点,因为这样才能保证不会陷入死循环,具体为什么自己实现一下就可以明白了。另外一个就是这道题目要开 \(\text{long long}\),而且极大值不能赋小了。

\(\texttt{Code}\)

#include <bits/stdc++.h>
using namespace std; #define INF 0x7f7f7f7f7f7f7f
#define Int register int
#define int long long
#define MAXN 200005 template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');} int n,t,toop = 1,f[MAXN],p[MAXN],q[MAXN],l[MAXN],fa[MAXN],to[MAXN],wei[MAXN],nxt[MAXN],dis[MAXN],head[MAXN];
void Add_Edge (int u,int v,int w){to[++ toop] = v,wei[toop] = w,nxt[toop] = head[u],head[u] = toop;}
void getdis (int u){for (Int i = head[u];i;i = nxt[i]) dis[to[i]] = dis[u] + wei[i],getdis (to[i]);} int top,sta[MAXN];double sl[MAXN];//储存每个点到下一个点的斜率
double Slope (int x,int y){return (f[y] - f[x]) * 1.0 / (dis[y] - dis[x]);}
void ins (int x){
while (top > 1 && sl[top - 1] <= Slope (sta[top],x)) -- top;
sta[++ top] = x,sl[top - 1] = Slope (sta[top - 1],x),sl[top] = -INF;
}
int query (double num){
int l = 1,r = top,ans = 0;
while (l <= r){
int mid = (l + r) >> 1;
if (sl[mid] <= num) ans = mid,r = mid - 1;
else l = mid + 1;
}
return sta[ans];
} int root,mxsiz,siz[MAXN];bool vis[MAXN];//淀粉质需要的东西 void findroot (int u,int SZ){
siz[u] = 1;int mx = 0;
for (Int i = head[u];i;i = nxt[i]) if (!vis[to[i]]) findroot (to[i],SZ),siz[u] += siz[to[i]],mx = max (mx,siz[to[i]]);
mx = max (mx,SZ - siz[u]);
if (mx <= mxsiz) mxsiz = mx,root = u;
} int sum,pot[MAXN]; void getpoint (int u){
pot[++ sum] = u;
for (Int i = head[u];i;i = nxt[i]) if (!vis[to[i]]) getpoint (to[i]);
} bool cmp (int x,int y){return dis[x] - l[x] > dis[y] - l[y];}//按照可以到的祖先深度排序 void work (int now,int SZ){
if (SZ == 1) return ;
mxsiz = INF,findroot (now,SZ);int x = root;
for (Int i = head[x];i;i = nxt[i]) vis[to[i]] = 1,SZ -= siz[to[i]];
work (now,SZ),sum = 0;
for (Int i = head[x];i;i = nxt[i]) getpoint (to[i]);
sort (pot + 1,pot + sum + 1,cmp);int a = x;top = 0;
for (Int i = 1;i <= sum;++ i){
int u = pot[i];
while (a != fa[now] && dis[a] >= dis[u] - l[u]) ins (a),a = fa[a];
if (top){
int k = query (p[u]);
f[u] = min (f[u],f[k] + (dis[u] - dis[k]) * p[u] + q[u]);
}
}
for (Int i = head[x];i;i = nxt[i]) work (to[i],siz[to[i]]);
} signed main(){
read (n,t);
for (Int i = 2,val;i <= n;++ i) read (fa[i],val,p[i],q[i],l[i]),Add_Edge (fa[i],i,val),f[i] = INF;
getdis (1),work (1,n);
for (Int i = 2;i <= n;++ i) write (f[i]),putchar ('\n');
return 0;
}

题解 [NOI2014]购票的更多相关文章

  1. [BZOJ3672][UOJ#7][NOI2014]购票

    [BZOJ3672][UOJ#7][NOI2014]购票 试题描述  今年夏天,NOI在SZ市迎来了她30周岁的生日.来自全国 n 个城市的OIer们都会从各地出发,到SZ市参加这次盛会.       ...

  2. 【BZOJ 3672】 3672: [Noi2014]购票 (CDQ分治+点分治+斜率优化)**

    3672: [Noi2014]购票 Description  今年夏天,NOI在SZ市迎来了她30周岁的生日.来自全国 n 个城市的OIer们都会从各地出发,到SZ市参加这次盛会.        全国 ...

  3. 【BZOJ3672】[Noi2014]购票 树分治+斜率优化

    [BZOJ3672][Noi2014]购票 Description  今年夏天,NOI在SZ市迎来了她30周岁的生日.来自全国 n 个城市的OIer们都会从各地出发,到SZ市参加这次盛会.       ...

  4. BZOJ 3672[NOI2014]购票(树链剖分+线段树维护凸包+斜率优化) + BZOJ 2402 陶陶的难题II (树链剖分+线段树维护凸包+分数规划+斜率优化)

    前言 刚开始看着两道题感觉头皮发麻,后来看看题解,发现挺好理解,只是代码有点长. BZOJ 3672[NOI2014]购票 中文题面,题意略: BZOJ 3672[NOI2014]购票 设f(i)f( ...

  5. $NOI2014$ 购票(斜率优化 点分治)

    \(NOI2014\)购票 哇终于可以碰电脑了赶快切些火题找找感觉. 拿到这道题的时候发现简单的斜率优化推一推可以秒掉平方做法,然后一条链也可以做. 然后呢... 卧槽这个在一棵树上怎么办啊. 大力\ ...

  6. bzoj 3672: [Noi2014]购票 树链剖分+维护凸包

    3672: [Noi2014]购票 Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 480  Solved: 212[Submit][Status][D ...

  7. BZOJ 3672: [Noi2014]购票( 树链剖分 + 线段树 + 凸包 )

    s弄成前缀和(到根), dp(i) = min(dp(j) + (s(i)-s(j))*p(i)+q(i)). 链的情况大家都会做...就是用栈维护个下凸包, 插入时暴力弹栈, 查询时就在凸包上二分/ ...

  8. bzoj千题计划251:bzoj3672: [Noi2014]购票

    http://www.lydsy.com/JudgeOnline/problem.php?id=3672 法一:线段树维护可持久化单调队列维护凸包 斜率优化DP 设dp[i] 表示i号点到根节点的最少 ...

  9. [BZOJ3672][Noi2014]购票 斜率优化+点分治+cdq分治

    3672: [Noi2014]购票 Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 1749  Solved: 885[Submit][Status][ ...

随机推荐

  1. ❤️用武侠小说的形式来阅读LinkedList的源码,绝了!

    一.LinkedList 的剖白 大家好,我是 LinkedList,和 ArrayList 是同门师兄弟,但我俩练的内功却完全不同.师兄练的是动态数组,我练的是链表. 问大家一个问题,知道我为什么要 ...

  2. elsa core—3.elsa 服务

    在本快速入门中,我们将介绍一个用于设置Elsa Server的最小ASP.NET Core应用程序.我们还将安装一些更常用的activities(活动),如Timer.Cron和sendmail,以能 ...

  3. Mysql常用sql语句(15)- cross join 交叉连接

    测试必备的Mysql常用sql语句 https://www.cnblogs.com/poloyy/category/1683347.html 前言 交叉连接就是求多表之间的笛卡尔积 讲道理..这个我都 ...

  4. IPSEC的实现方式

    IPSEC的实现方式 在IPSEC通信中涉及到一个重要方面,那就是如何定义要保护的数据流(又称为感兴趣流).这不仅涉及到IPSEC最终要保护哪部分数据,还关系到IPSEC的实现方式,因此有必要把感兴趣 ...

  5. 第三课:GDB 常用的调试命令概览

    先给出一个常用命令的列表,后面会结合具体的例子详细介绍每个命令的用法. 命令名称 命令缩写 命令说明 run r 运行一个程序 continue c 让暂停的程序继续运行 next n 运行到下一行 ...

  6. k8s资源管理(基础操作)

    1. 基础 本文实操基于k8s 1.22.1 # 可以查看资源分配情况 kubectl describe node # 全局资源情况查看 kubectl api-resources 1.1 apply ...

  7. 轻量化模型系列--GhostNet:廉价操作生成更多特征

    ​  前言  由于内存和计算资源有限,在嵌入式设备上部署卷积神经网络 (CNN) 很困难.特征图中的冗余是那些成功的 CNN 的一个重要特征,但在神经架构设计中很少被研究. 论文提出了一种新颖的 Gh ...

  8. Lua io.lines()

    前言# 从文章的题目可以看出,今天的内容是和文件的行相关的,其实这个函可以看成是一个文件读取函数,只不过文件读取的形式固定了,就是只能一行一行的读,接下来我们就一起来看看这个函数究竟要怎么使用. 内容 ...

  9. Java跨平台原理(字节码文件,虚拟机)

    介绍 C/C++语言都直接编译成针对特定平台机器码.如果要跨平台,需要使用相应的编译器重新编译. Java源程序(.java)要先编译成与平台无关的字节码文件(.class),然后字节码文件再解释成机 ...

  10. Spring Cloud Hystrix 学习(二)熔断与降级

    今天来看下Hystrix的熔断与降级. 首先什么是降级?当请求超时.资源不足等情况发生时进行服务降级处理,不调用真实服务逻辑,而是使用快速失败(fallback)方式直接返回一个托底数据,保证服务链条 ...