BZOJ 3672 购票
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 \times 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}) \rbrace$$
然后化简一下就可得到$$f_{i}=min \lbrace f_{j}+P_{i}d_{i}+q_{i}-P_{i}d_{j} \rbrace$$
令$$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\n",f[i]);
fclose(stdin); fclose(stdout);
return 0;
}