重写一遍很久以前写过的题。
考虑链上的问题。容易想到设f[i]为i到1的最少购票费用,转移有f[i]=min{f[j]+(dep[i]-dep[j])*p[i]+q[i]} (dep[i]-dep[j]<=l[i])。套路的考虑若j转移优于k(dep[j]>dep[k]),则f[j]-dep[j]*p[i]<f[k]-dep[k]*p[i],f[j]-f[k]<(dep[j]-dep[k])*p[i],(f[j]-f[k])/(dep[j]-dep[k])<p[i]。若没有l[]的限制,对(dep[],f[])维护一个下凸壳即可。加入l[]的限制后,考虑使用cdq分治,右侧按dep[]-l[]从大到小排序,更新点时将新增的可以用来更新的点加入凸壳,并在凸壳上二分。
拓展到树上,通过点分治实现一个树上cdq即可,同理要先处理靠近根的部分。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define N 200010
#define int long long
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
int read()
{
int x=0,f=1;char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x*f;
}
int n,p[N],t,fa[N],w[N],v[N],lim[N],deep[N],f[N],size[N],q[N],u[N],cnt2;
bool flag[N];
struct data{int to,nxt,len;
}edge[N<<1];
void addedge(int x,int y,int z){t++;edge[t].to=y,edge[t].nxt=p[x],edge[t].len=z,p[x]=t;}
void dfs(int k)
{
for (int i=p[k];i;i=edge[i].nxt)
if (edge[i].to!=fa[k])
{
deep[edge[i].to]=deep[k]+edge[i].len;
dfs(edge[i].to);
}
}
void make(int k,int from)
{
size[k]=1;
for (int i=p[k];i;i=edge[i].nxt)
if (edge[i].to!=from&&!flag[edge[i].to])
{
make(edge[i].to,k);
size[k]+=size[edge[i].to];
}
}
int findroot(int k,int from,int s)
{
int mx=0;
for (int i=p[k];i;i=edge[i].nxt)
if (edge[i].to!=from&&!flag[edge[i].to]&&size[edge[i].to]>size[mx]) mx=edge[i].to;
if ((size[mx]<<1)>s) return findroot(mx,k,s);
else return k;
}
void build(int k,int from,int *id,int &cnt)
{
id[++cnt]=k;
for (int i=p[k];i;i=edge[i].nxt)
if (edge[i].to!=from&&!flag[edge[i].to]&&deep[edge[i].to]<deep[k]) build(edge[i].to,k,id,cnt);
}
long double slope(int x,int y){return (long double)(f[x]-f[y])/(deep[x]-deep[y]);}
void get(int k,int from)
{
u[++cnt2]=k;
for (int i=p[k];i;i=edge[i].nxt)
if (edge[i].to!=from&&!flag[edge[i].to]) get(edge[i].to,k);
}
bool cmp(const int&a,const int&b)
{
return deep[a]-lim[a]>deep[b]-lim[b];
}
void solve(int k)
{
make(k,k);k=findroot(k,k,size[k]);flag[k]=1;
int id[N],cnt;id[cnt=1]=k;
for (int i=p[k];i;i=edge[i].nxt)
if (!flag[edge[i].to]&&deep[edge[i].to]<deep[k])
{
build(edge[i].to,edge[i].to,id,cnt);
solve(edge[i].to);
}
for (int i=2;i<=cnt;i++) if (deep[k]-deep[id[i]]<=lim[k]) f[k]=min(f[k],f[id[i]]+w[k]*(deep[k]-deep[id[i]])+v[k]);
cnt2=0;
for (int i=p[k];i;i=edge[i].nxt)
if (!flag[edge[i].to]) get(edge[i].to,edge[i].to);
sort(u+1,u+cnt2+1,cmp);
int t=0,tail=0;
for (int i=1;i<=cnt2;i++)
{
while (t<cnt&&deep[id[t+1]]>=deep[u[i]]-lim[u[i]])
{
while (tail>1&&slope(id[t+1],q[tail])>slope(q[tail],q[tail-1])) tail--;
q[++tail]=id[++t];
}
if (tail)
{
int l=1,r=tail-1,ans=tail;
while (l<=r)
{
int mid=l+r>>1;
if (slope(q[mid+1],q[mid])<w[u[i]]) ans=mid,r=mid-1;
else l=mid+1;
}
f[u[i]]=min(f[u[i]],f[q[ans]]+w[u[i]]*(deep[u[i]]-deep[q[ans]])+v[u[i]]);
}
}
cnt=0;
for (int i=p[k];i;i=edge[i].nxt)
if (!flag[edge[i].to]) solve(edge[i].to);
}
signed main()
{
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
const char LL[]="%I64d
";
#else
const char LL[]="%lld
";
#endif
n=read();read();
for (int i=2;i<=n;i++)
{
fa[i]=read();
addedge(fa[i],i,read());
addedge(i,fa[i],0);
w[i]=read(),v[i]=read(),lim[i]=read();
}
dfs(1);
memset(f,60,sizeof(f));f[1]=0;
solve(1);
for (int i=2;i<=n;i++) printf(LL,f[i]);
return 0;
}