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),只有当它们之间所有道路的总长度不超过(lv)时,从城市(v)才可以通过一次购票到达城市(a),否则不能通过一次购票到达。对于每个城市(v),我们还会给出两个非负整数(p_{v},q_{v})作为票价参数。若城市(v)到城市(a)所有道路的总长度为(d),那么从城市(v)到城市 (a)购买的票价为(d imes p_{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市。
Sample Input
7 3
1 2 20 0 3
1 5 10 100 5
2 4 10 10 10
2 9 1 100 10
3 5 20 100 10
4 4 20 0 10
Sample Output
40
150
70
149
300
150
Hint
首先另(d_{i})表示(i)的(1)的距离,我们可以先写出dp方程$$f_{i} = min lbrace f_{j}+(d_{i}-d_{j}P_{i}+Q_{j})
brace$$
然后化简一下就可得到$$f_{i}=min lbrace f_{j}+P_{i}d_{i}+q_{i}-P_{i}d_{j}
brace$$
令$$x = d_{j},y = f_{j}+P_{i}d_{}+Q_{i},k = P_{i},b = f_{i}$$就是标准的斜率优化(b=y-kx)的式子了。维护一个下凸壳即可。
于是考虑如何维护这个下凸壳,由于对(i)有用的(j)一定是(i)的祖先,所以明显从上往下更新,于是就有树链剖分。
但是好像不好动态维护凸壳,但是我们发现从上往下加点(x=d_{i})是递增的,所以没必要二分,直接叉积就行了。
至于满足(L_{i})的限制,直接在树上用树链剖分找出一个满足条件的最高点,再做询问就没问题了。时间复杂度(O(nlog^{3}n))达不到上界。
#include<iostream>
#include<cmath>
#include<vector>
#include<cstdio>
#include<cstdlib>
using namespace std;
typedef long long ll;
#define eps (1e-6)
#define maxn (400010)
int N,side[maxn],toit[maxn],next[maxn],cnt,size[maxn],id[maxn],father[maxn];
int top[maxn]; ll len[maxn],d[maxn],f[maxn],Map[maxn],P[maxn],Q[maxn],L[maxn];
struct node
{
ll x,y;
friend inline int operator /(const node &a,const node &b)
{
double res = 1.0*a.x*b.y-1.0*b.x*a.y;
if (fabs(res-0) <= eps) return 0;
else if (res > 0) return 1; else return -1;
}
friend inline node operator -(const node &a,const node &b) { return (node){a.x-b.x,a.y-b.y}; }
inline double angle() { return 1.0*y/x; }
};
vector <node> convex[maxn*2]; vector <double> slop[maxn*2];
inline void add(int a,int b,ll c) { next[++cnt] = side[a]; side[a] = cnt; toit[cnt] = b; len[cnt] = c; }
inline void ins(int a,int b,ll c) { add(a,b,c); add(b,a,c); }
inline ll read()
{
char ch; ll ret = 0,f = 1;
do ch = getchar(); while (!(ch >= '0'&&ch <= '9')&&ch != '-');
if (ch == '-') ch = getchar(),f = -1;
do ret = ret*10+(ll)(ch-'0'),ch = getchar(); while (ch >= '0'&&ch <= '9');
return f*ret;
}
inline void dfs(int now)
{
size[now] = 1;
for (int i = side[now];i;i = next[i])
{
if (toit[i] == father[now]) continue;
father[toit[i]] = now; d[toit[i]] = d[now]+len[i];
dfs(toit[i]); size[now] += size[toit[i]];
}
}
inline void Div(int now,int Top)
{
top[now] = Top; id[now] = ++cnt; Map[cnt] = now;
int heavy = 0;
for (int i = side[now];i;i = next[i])
if (toit[i] != father[now]&&size[toit[i]] > size[heavy]) heavy = toit[i];
if (!heavy) return; Div(heavy,Top);
for (int i = side[now];i;i = next[i]) if (toit[i] != father[now]&&toit[i] != heavy) Div(toit[i],toit[i]);
}
inline ll br(int now,ll key)
{
int l = 0,r = slop[now].size()-1,mid;
while (l <= r)
{
mid = (l+r) >> 1;
if (slop[now][mid] < key) l = mid+1;
else r = mid-1;
}
return convex[now][l].y-key*convex[now][l].x;
}
inline ll query(int now,int l,int r,int ql,int qr,ll key)
{
if (ql <= l&&r <= qr) return br(now,key);
int mid = (l+r)>>1;
if (qr <= mid) return query(now<<1,l,mid,ql,qr,key);
else if (ql > mid) return query(now<<1|1,mid+1,r,ql,qr,key);
else return min(query(now<<1,l,mid,ql,mid,key),query(now<<1|1,mid+1,r,mid+1,qr,key));
}
inline void insert(int now,int l,int r,int pos,const node &pp)
{
int nn = convex[now].size();
while (nn>1&&(pp-convex[now][nn-1])/(convex[now][nn-1]-convex[now][nn-2]) >= 0)
--nn,convex[now].pop_back(),slop[now].pop_back();
++nn; convex[now].push_back(pp);
if (nn > 1) slop[now].push_back((convex[now][nn-1]-convex[now][nn-2]).angle());
if (l == r) return; int mid = (l+r) >> 1;
if (pos <= mid) insert(now<<1,l,mid,pos,pp);
else insert(now<<1|1,mid+1,r,pos,pp);
}
inline void work(int now)
{
int Top = now,i; f[now] = 1LL<<60;
while (true)
{
if (L[now] >= d[Top]-d[father[top[Top]]]) L[now] -= d[Top]-d[father[top[Top]]],Top = father[top[Top]];
else
{
int l = id[top[Top]],r = id[Top],mid;
while (l <= r)
{
mid = (l+r)>>1;
if (d[Top]-d[Map[mid]] > L[now]) l = mid+1;
else r = mid-1;
}
Top = Map[l]; break;
}
}
if (Top == now) return;
for (i = father[now];top[i] != top[Top];i = father[top[i]])
f[now] = min(query(1,1,N,id[top[i]],id[i],P[now]),f[now]);
f[now] = min(query(1,1,N,id[Top],id[i],P[now]),f[now]);
f[now] += P[now]*d[now]+Q[now];
}
inline void Dfs(int now)
{
work(now);
insert(1,1,N,id[now],(node){d[now],f[now]});
for (int i = side[now];i;i = next[i])
if (toit[i] != father[now]) Dfs(toit[i]);
}
int main()
{
freopen("3672.in","r",stdin);
freopen("3672.out","w",stdout);
N = read(); read();
for (int i = 2,a,b;i <= N;++i)
{
a = read(),b = read(),ins(i,a,b);
P[i] = read(),Q[i] = read(),L[i] = read();
}
d[cnt = 0] = -1LL << 60; dfs(1); Div(1,1); insert(1,1,N,id[1],(node){0,0});
for (int i = side[1];i;i = next[i]) Dfs(toit[i]);
for (int i = 2;i <= N;++i) printf("%lld
",f[i]);
fclose(stdin); fclose(stdout);
return 0;
}