重写一遍很久以前写过的题。
考虑链上的问题。容易想到设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; }