这个斜率优化式很简单,推一下就出来了,跟陶陶的难题II很像。
接下来就在线段树上维护凸包就好了。查询的时候在线段树上找到对应区间,然后在凸包上二分。
为了方便,我写了可撤销的线段树+凸包。
时间复杂度O(n(log2n)2)O(n(log_2n)^2)O(n(log2n)2)。
#pragma GCC optimize(3)
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cassert>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=200005;
int n,type,cnt,fa[N][20],head[N],dep[N],to[N],nxt[N];
ll fd[N],p[N],q[N],l[N],f[N],dis[N],siz1[N*4],siz2[N*4];
struct data{
ll x,y;
}a[N];
int* v1[N*4];
struct operation{
bool type;//0:push 1:pop
int id,from;
};
operation* v2[N*4];
void adde(int u,int v){
to[++cnt]=v;
nxt[cnt]=head[u];
head[u]=cnt;
}
void dfs(int u){
dep[u]=dep[fa[u][0]]+1;
dis[u]=dis[fa[u][0]]+fd[u];
for(int i=1;(1<<i)<=dep[u];i++){
fa[u][i]=fa[fa[u][i-1]][i-1];
}
int v;
for(int i=head[u];i;i=nxt[i]){
v=to[i];
dfs(v);
}
}
int jump(int u,ll d){
for(int i=19;i>=0;i--){
if(fa[u][i]&&dis[u]-dis[fa[u][i]]<=d){
d-=dis[u]-dis[fa[u][i]];
u=fa[u][i];
}
}
return u;
}
double getx(data a,data b){
return b.x-a.x;
}
double gety(data a,data b){
return b.y-a.y;
}
void build(int o,int l,int r){
v1[o]=new int[r-l+1];
v2[o]=new operation[2*(r-l+1)];
if(l==r){
return;
}
int mid=(l+r)/2;
build(o*2,l,mid);
build(o*2+1,mid+1,r);
}
void update(int o,int l,int r,int k,int id){
while(siz1[o]>1&&gety(a[v1[o][siz1[o]]],a[id])*getx(a[v1[o][siz1[o]-1]],a[v1[o][siz1[o]]])<=gety(a[v1[o][siz1[o]-1]],a[v1[o][siz1[o]]])*getx(a[v1[o][siz1[o]]],a[id])){
v2[o][++siz2[o]]=(operation){1,id,v1[o][siz1[o]]};
siz1[o]--;
}
v1[o][++siz1[o]]=id;
v2[o][++siz2[o]]=(operation){0,id};
if(l==r){
return;
}
int mid=(l+r)/2;
if(k<=mid){
update(o*2,l,mid,k,id);
}else{
update(o*2+1,mid+1,r,k,id);
}
}
void reset(int o,int l,int r,int k,int id){
operation now;
while(siz2[o]){
now=v2[o][siz2[o]];
if(now.id!=id){
break;
}
if(!now.type){
siz1[o]--;
}else{
v1[o][++siz1[o]]=now.from;
}
siz2[o]--;
}
if(l==r){
return;
}
int mid=(l+r)/2;
if(k<=mid){
reset(o*2,l,mid,k,id);
}else{
reset(o*2+1,mid+1,r,k,id);
}
}
ll get(int o,int id){
int l=1,r=siz1[o],mid;
while(l<r){
mid=(l+r)/2;
if(gety(a[v1[o][mid]],a[v1[o][mid+1]])>p[id]*getx(a[v1[o][mid]],a[v1[o][mid+1]])){
r=mid;
}else{
l=mid+1;
}
}
return f[v1[o][r]]+p[id]*(dis[id]-dis[v1[o][r]])+q[id];
}
ll query(int o,int l,int r,int L,int R,int id){
if(L==l&&R==r){
return get(o,id);
}
int mid=(l+r)/2;
if(R<=mid){
return query(o*2,l,mid,L,R,id);
}else if(L>mid){
return query(o*2+1,mid+1,r,L,R,id);
}else{
return min(query(o*2,l,mid,L,mid,id),query(o*2+1,mid+1,r,mid+1,R,id));
}
}
void solve(int u){
if(u!=1){
int anc=jump(u,l[u]);
f[u]=query(1,1,n,dep[anc],dep[u]-1,u);
}
a[u]=(data){dis[u],f[u]};
update(1,1,n,dep[u],u);
int v;
for(int i=head[u];i;i=nxt[i]){
v=to[i];
solve(v);
}
reset(1,1,n,dep[u],u);
}
int main(){
scanf("%d%d",&n,&type);
for(int i=2;i<=n;i++){
scanf("%d%lld%lld%lld%lld",&fa[i][0],&fd[i],&p[i],&q[i],&l[i]);
adde(fa[i][0],i);
}
dfs(1);
build(1,1,n);
solve(1);
for(int i=2;i<=n;i++){
printf("%lld\n",f[i]);
}
return 0;
}
``