Description:
今年夏天,NOI在SZ市迎来了她30周岁的生日。来自全国 n 个城市的OIer们都会从各地出发,到SZ市参加这次盛会。
全国的城市构成了一棵以SZ市为根的有根树,每个城市与它的父亲用道路连接。为了方便起见,我们将全国的 n 个城市用 1 到 n 的整数编号。其中SZ市的编号为 1。对于除SZ市之外的任意一个城市 v,我们给出了它在这棵树上的父亲城市 $f_v$ 以及到父亲城市道路的长度 $s_v$。
从城市 v 前往SZ市的方法为:选择城市 v 的一个祖先 a,支付购票的费用,乘坐交通工具到达 a。再选择城市 a 的一个祖先 b,支付费用并到达 b。以此类推,直至到达SZ市。
对于任意一个城市 v,我们会给出一个交通工具的距离限制 $l_v$。对于城市 v 的祖先 a,只有当它们之间所有道路的总长度不超过 $l_v$ 时,从城市 v 才可以通过一次购票到达城市 a,否则不能通过一次购票到达。对于每个城市 v,我们还会给出两个非负整数 $p_v$,$q_v$ 作为票价参数。若城市 v 到城市 a 所有道路的总长度为 d,那么从城市 v 到城市 a 购买的票价为 $dp_v+q_v$。
每个城市的OIer都希望自己到达SZ市时,用于购票的总资金最少。你的任务就是,告诉每个城市的OIer他们所花的最少资金是多少。
Input:
输入文件的第1行包含2个非负整数n,t,分别表示城市的个数和数据类型(其意义将在后面提到)。 输入文件的第2到n行,每行描述一个除SZ之外的城市。其中第v行包含5个非负整数$f_v,s_v,p_v,q_v,l_v$,分别表示城市v的父亲城市,它到父亲城市道路的长度,票价的两个参数和距离限制。
请注意:输入不包含编号为1的SZ市,第2行到第n行分别描述的是城市2到城市n。
Output:
输出包含n−1行,每行包含一个整数。其中第v行表示从城市v+1出发,到达SZ市最少的购票费用。
同样请注意:输出不包含编号为1的SZ市。
Analysis:
不难想到DP方程如下:
$f[i] = min(f[j], (D_i-D_j)*p_i+q_i)$
其中,$D_i$为根到$i$的距离,且$j \in anc(i)$,$(D_i-D_j) <= l_i$
运用斜率优化的套路,设有决策$j$,$k$,且$dep_j > dep_k$,$k$优于$j$当且仅当:
可化为 $\frac{f[k]-f[j]}{D_k-D_j} > p_i$,这是斜率DP的基本形式,注意到$D_i$是单调的,而$p_i$不是,说明在具体实现时,我们不能从队尾弹出元素,并且对于一个$p_i$,需要用二分找到它的最优解
接下来,我们发现DP的东西是一棵树,不同于线性DP,需要用树的点分治来处理:
1.首先找到树的重心$H$,把树从这里砍开(注意要避免被菊花图卡掉,需要把$H$和根放在一起,然后砍开$H$的所有子节点)
2.把$H$和根的一块继续递归处理。
3.把$H$下面的众子节点们按所能到达的最小祖先深度排序,深度越大排在越后面
4.按顺序处理众子节点们,从$H$开始,每次把还未加入的自己可达的祖先们(决策们)加进去,并更新$f$(用斜率DP+二分处理)
5.递归处理$H$的子节点们的子树
(ps: 我们维护的本来是一个正常的下凸壳,但是由于D值是由大往小加的[意思就是横坐标是从右往左加入的],所以维护的是一个上凸壳)
Thoughts:
我神奇地认为自己非常好的理解了这题(勿喷。。)
终于好像搞明白了斜率DP???(某斜率 > 某值,则斜率递增为下凸壳,某斜率f < 某值,反之 ->-> 实际上就是要维护后面的都不比之前的优吧,一个不比一个优,如果加一个新的不满足凸性,那么说明新的比某老的优。。就要把老的弹掉,因为老的没有用了啊哈哈。。至于队尾弹。是因为某值变了啊,并且如果你要弹掉队尾的话,某值还要单调,否则你可能把后面你要用的符合条件的东西弹掉了,像这题某值不单调,就二分找第一个满足条件的,找到以后因为斜率是。。。的,所以一个不比一个优,所以就是找的的第一个满足条件的就是最优的决策)<-<-我废话好多。。
不过这样子在树的结构上运用树的点分治作DP,是个非常好的思路!
还有找重心就是dfs一遍然后找出子节点个数,和用总数减去子节点个数就可以统计,也非常好
Code:
//2017.5.25 Miaomiao
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define LL long long
#define For(i, a, b) for(int i = (a); i <= (int)(b); ++i)
#define N (200000+5)
LL p[N], q[N], lim[N], dp[N], dis[N], siz[N];
int fa[N], A[N], B[N], tu[N];
int en, Begin[N], Nxt[N], To[N];
bool vis[N];
inline bool Chkmin(int &a, int b){if(a<b) return true; a=b; return false;}
inline void Add(int a, int b){
To[++en] = b; Nxt[en] = Begin[a]; Begin[a] = en;
}
inline bool Bcmp(int a, int b){
return dis[a]-lim[a] > dis[b]-lim[b];
}
inline double Slope(int b, int a){
return 1.0*(dp[b]-dp[a])/(dis[b]-dis[a]);
}
void To_Root_Dis(int now){
for(int i = Begin[now]; i; i = Nxt[i]){
dis[To[i]] += dis[now];
To_Root_Dis(To[i]);
}
}
int m, an, bn, hvy, Mins;
double st[N];
void FindHeavy(int now){
siz[now] = 1;
for(int i = Begin[now]; i; i = Nxt[i]){
if(vis[To[i]]) continue;
FindHeavy(To[i]); siz[now] += siz[To[i]];
}
if(!Chkmin(Mins, max(siz[now], m-siz[now]))) hvy = now;
}
void Dfs(int now){
B[++bn] = now;
for(int i = Begin[now]; i; i = Nxt[i]){
if(vis[To[i]]) continue;
Dfs(To[i]);
}
}
void Solve(int H){
if(m <= 1) return;
hvy = H; Mins = m;
FindHeavy(H);
int rt = fa[hvy];
for(int i = Begin[rt]; i; i = Nxt[i]) vis[To[i]] = true, m -= siz[To[i]];
Solve(H);
int tnow = rt;
an = bn = 0;
while(tnow != H) A[++an] = tnow, tnow = fa[tnow];
A[++an] = H;
for(int i = Begin[rt]; i; i = Nxt[i]) Dfs(To[i]);
sort(B+1, B+bn+1, Bcmp);
int bp = 1, now, tp;
while(bp <= bn && dis[B[bp]]-lim[B[bp]] > dis[rt]) ++bp;
tp = 0;
int L, R, mid, v, tx;
For(i, 1, an){
now = A[i];
while(tp>1 && Slope(now, tu[tp]) > st[tp]) --tp;
tu[++tp] = now; st[tp] = tp==1? -1.0*(1LL<<60): Slope(now, tu[tp-1]);
while(bp <= bn && (i==an || dis[B[bp]]-lim[B[bp]] > dis[A[i+1]])){
L = 1; R = tp; v = B[bp];
while(L < R){
mid = (L+R+1)>>1;
if(p[v] < st[mid]) L = mid;
else R = mid-1;
}
tx = tu[L];
dp[v] = min(dp[v], dp[tx]+p[v]*(dis[v]-dis[tx])+q[v]);
++bp;
}
}
for(int i = Begin[rt]; i; i = Nxt[i]) m = siz[To[i]], Solve(To[i]);
}
int main(){
#ifndef ONLINE_JUDGE
freopen("test.in", "r", stdin);
freopen("test.out", "w", stdout);
#endif
int n, t;
scanf("%d%d", &n, &t);
For(i, 2, n){
scanf("%d%lld%lld%lld%lld", &fa[i], &dis[i], &p[i], &q[i], &lim[i]);
Add(fa[i], i);
dp[i] = 1LL<<60;
}
To_Root_Dis(1);
m = n; Solve(1);
For(i, 2, n) printf("%lld\n", dp[i]);
return 0;
}