按照这类折线题的套路,我们分析加入一个点引起的图像变化
设
f
(
x
)
f(x)
f(x)表示最终长度为
x
x
x的最小代价
则需要考虑一个点的儿子对它有什么影响
显然
f
(
x
)
f(x)
f(x)为斜率变化为1的分段一次函数,考虑一个点加入到父亲的边之后产生的变化,设
f
(
x
)
f(x)
f(x)斜率为0的区间为
[
L
,
R
]
[L,R]
[L,R],到父亲的边的权值为
w
w
w,则有如下四种情况:
x
≤
L
:
f
′
(
x
)
=
f
(
x
)
+
w
x\le L:f'(x)=f(x)+w
x≤L:f′(x)=f(x)+w,显然直接继承
f
(
x
−
w
)
f(x-w)
f(x−w)不比删掉
w
w
w优,因为前面这段函数的斜率都
≤
−
1
\le-1
≤−1
L
≤
x
≤
L
+
w
:
f
′
(
x
)
=
f
(
L
)
+
w
−
(
x
−
L
)
L\le x \le L+w:f'(x)=f(L)+w-(x-L)
L≤x≤L+w:f′(x)=f(L)+w−(x−L),道理同上
L
+
w
≤
x
≤
R
+
w
:
f
′
(
x
)
=
f
(
L
)
L+w\le x \le R+w:f'(x)=f(L)
L+w≤x≤R+w:f′(x)=f(L),直接继承最小值
R
+
w
≤
x
:
f
′
(
x
)
=
f
(
R
)
+
x
−
(
R
−
w
)
R+w\le x:f'(x)=f(R)+x-(R-w)
R+w≤x:f′(x)=f(R)+x−(R−w),和第一种类似
并且我们每次只会改变右侧的点, 而这些点可以直接删除,所以用个可并堆就好了
Code:
#include<bits/stdc++.h>
#include<ext/pb_ds/priority_queue.hpp>
#define ll long long
using namespace std;
inline int read(){
int res=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f-=f;ch=getchar();}
while(isdigit(ch)) {res=(res<<1)+(res<<3)+(ch^48);ch=getchar();}
return res*f;
}
const int N=1e6+5;
ll ans;
int fa[N],siz[N],w[N];
__gnu_pbds::priority_queue<ll>q[N];
int main(){
int n=read(),m=read();
for(int i=2;i<=n+m;i++){
fa[i]=read(),w[i]=read();
siz[fa[i]]++;ans+=w[i];
}
for(int i=n+m;i>=2;i--){
ll x=0,y=0;
if(i<=n){
for(int j=1;j<siz[i];j++) q[i].pop();
x=q[i].top();q[i].pop();
y=q[i].top();q[i].pop();
}
q[fa[i]].push(x+w[i]);q[fa[i]].push(y+w[i]);
q[fa[i]].join(q[i]);
}
for(int i=1;i<=siz[1];i++) q[1].pop();
while(!q[1].empty()) ans-=q[1].top(),q[1].pop();
cout<<ans;
return 0;
}