我们设
cost(y,x)
c
o
s
t
(
y
,
x
)
表示从
y
y
刷到的代价,设
du=h−depth(u),
d
u
=
h
−
d
e
p
t
h
(
u
)
,
。
cost(y,x)=Cy⋅(dx(dx+1)2−dy(dy−1)2)+C2y⋅(dx−dy+1)−Hy
c
o
s
t
(
y
,
x
)
=
C
y
⋅
(
d
x
(
d
x
+
1
)
2
−
d
y
(
d
y
−
1
)
2
)
+
C
y
2
⋅
(
d
x
−
d
y
+
1
)
−
H
y
cost(y,x)=12(Cy⋅d2x+(Cy+2⋅C2y)⋅dx+2⋅(−Cy⋅dy(dy−1)2+C2y⋅(1−dy)−Hy))
c
o
s
t
(
y
,
x
)
=
1
2
(
C
y
⋅
d
x
2
+
(
C
y
+
2
⋅
C
y
2
)
⋅
d
x
+
2
⋅
(
−
C
y
⋅
d
y
(
d
y
−
1
)
2
+
C
y
2
⋅
(
1
−
d
y
)
−
H
y
)
)
那么 cost(y,x) c o s t ( y , x ) 就化成了一个关于 dx d x 的二次函数的形式, 2cost(y,x)=Ad2x+Bdx+C 2 c o s t ( y , x ) = A d x 2 + B d x + C ,其中
A=Cy
A
=
C
y
B=Cy+2⋅C2y
B
=
C
y
+
2
⋅
C
y
2
C=2⋅(−Cy⋅dy(dy−1)2+C2y⋅(1−dy)−Hy)
C
=
2
⋅
(
−
C
y
⋅
d
y
(
d
y
−
1
)
2
+
C
y
2
⋅
(
1
−
d
y
)
−
H
y
)
观察两个不同的贡献函数 cost(y,x),cost(y′,x) c o s t ( y , x ) , c o s t ( y ′ , x ) ,其做差后 ΔA=Cy−Cy′,ΔB=Cy−Cy′+2⋅(C2y−C2y′) Δ A = C y − C y ′ , Δ B = C y − C y ′ + 2 ⋅ ( C y 2 − C y ′ 2 ) ,可以发现对称轴 −ΔB2ΔA=−1+(Cy+C′y)2<0 − Δ B 2 Δ A = − 1 + ( C y + C y ′ ) 2 < 0 ,说明两函数在正半轴只有一个交点,而且决策关于 Ci C i 单调。
于是我们可以用类似斜率优化DP的单调队列做法,用一个set维护按 Ci C i 有序的决策集合。多个子树set可以用启发式合并,就是暴力把小集合里的元素拿出来插入到大集合里,并左右判一下即可。对于整个set加一个值可以直接打lazy标记。
代码:
#include<bits/stdc++.h>
#define N 200010
#define ll long long
#define its set<node>::iterator
#define rits set<node>::reverse_iterator
using namespace std;
int n,tote,mxd,con[N],nxt[N<<1],to[N<<1],d[N],sz[N],id[N];
ll C[N],H[N],f[N],lazy[N];
struct node
{
ll c,h;
friend bool operator <(const node a,const node b)
{
if(a.c==b.c) return a.h>b.h;
return a.c>b.c;
}
};
set<node> s[N];
bool chkmin(ll &a,ll b)
{
return (a<=b?0:(a=b,1));
}
ll cal(node a,ll x)
{
return a.c*x*x+(a.c+a.c*a.c*2)*x+a.h;
}
double inter(node a,node b)
{
double A=a.c-b.c,B=a.c-b.c+(a.c*a.c-b.c*b.c)*2,C=a.h-b.h;
return (sqrt(B*B-A*C*4)*(A>0?1:-1)-B)/(A*2);
}
void adde(int x,int y)
{
to[++tote]=y;
nxt[tote]=con[x];
con[x]=tote;
}
its pre(its x)
{
its t=x;t--;
return t;
}
its suc(its x)
{
its t=x;t++;
return t;
}
bool check(node a,node b)
{
if(a.c<=b.c&&a.h<=b.h) return 1;
return 0;
}
void dfs0(int v,int fa)
{
mxd=max(d[v],mxd);
sz[v]=1;
for(int p=con[v];p;p=nxt[p])
if(to[p]!=fa)
d[to[p]]=d[v]+1,dfs0(to[p],v),sz[v]+=sz[to[p]];
}
void ins(set<node> &a,node p)
{
its t0=a.insert(p).first;
if(t0!=a.begin()&&suc(t0)!=a.end())
if(check(*suc(t0),*t0)||inter(*suc(t0),*t0)<=inter(*t0,*pre(t0))) {a.erase(t0);return ;}
if(t0!=a.begin())
while(pre(t0)!=a.begin())
if(check(*t0,*pre(t0))||inter(*t0,*pre(t0))<=inter(*pre(t0),*pre(pre(t0)))) a.erase(pre(t0));
else break;
if(suc(t0)!=a.end())
while(suc(suc(t0))!=a.end())
if(inter(*suc(suc(t0)),*suc(t0))<=inter(*suc(t0),*t0)) a.erase(suc(t0));
else break;
}
void dp(int v,int fa)
{
ll tot=0,hs=0;
for(int p=con[v];p;p=nxt[p])
if(to[p]!=fa)
{
dp(to[p],v),tot+=f[to[p]];
if(sz[to[p]]>sz[hs]) hs=to[p];
}
for(int p=con[v];p;p=nxt[p])
if(to[p]!=fa)
{
its t;
lazy[id[to[p]]]+=tot-f[to[p]];
for(t=s[id[to[p]]].begin();suc(t)!=s[id[to[p]]].end();)
if(inter(*suc(t),*t)>d[v]) break;
else s[id[to[p]]].erase(t++);
chkmin(f[v],cal(*t,d[v])+lazy[id[to[p]]]);
}
if(!hs) id[v]=v;
else
{
id[v]=id[hs];
for(int p=con[v];p;p=nxt[p])
if(to[p]!=fa&&to[p]!=hs)
for(its t=s[id[to[p]]].begin();t!=s[id[to[p]]].end();t++)
{
node x=(*t);
x.h+=lazy[id[to[p]]]-lazy[id[v]];
ins(s[id[v]],x);
}
}
node x;
x.c=C[v];
x.h=2ll*(-H[v]+C[v]*C[v]*(1ll-d[v])-C[v]*(d[v]-1ll)*d[v]/2ll);
chkmin(f[v],cal(x,d[v])+tot);
x.h+=tot-lazy[id[v]];
ins(s[id[v]],x);
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%lld%lld",&H[i],&C[i]);
for(int i=1;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
adde(x,y);adde(y,x);
}
memset(f,0x3f,sizeof(f));
dfs0(1,0);
for(int i=1;i<=n;i++)
d[i]=mxd-d[i];
dp(1,0);
printf("%lld",f[1]>>1);
return 0;
}