Description
给你一棵有根树。每个节点有个权值。如果你花费1块钱,你就可以对某个节点的权值加1或减1。
现在,你的目标是用最小的花费,使得:对于这棵树上的每一个顶点,它的儿子的权值严格小于它本身的权值。
Input
第一行输入两个整数 N,C1 ,分别表示树上的节点数和根的权值。节点标号从1到N。根节点为1。
第 2 行至第 N 行:每行两个整数 Pi,Ci 。其中 Pi 表示 i 节点的父亲, Ci 表示 i 节点的权值。
Output
输出一个整数,表示最小花费。
Sample Input
8 6
1 1
2 1
2 3
1 9
5 6
6 6
6 2
Sample Output
8
这题我们考虑用左偏树维护答案。
这题是树上的左偏树,还有一道题是这道题的弱化版(链上版本):【BZOJ1367】【Baltic2004】sequence
然后用三个数组:root,root1,root2来表示答案。
三个数组表示的含义(在左偏树上):
val的含义:其实就是每个节点的值,但是为了满足是父亲严格大于儿子,所以我们在dfs的时候还要加上每个节点的deep。
root:表示的是这个点的对应的val值(也就是用来寻找这个节点在统计答案时用自身的val减去哪一个节点的val来得到答案,下同)。
root1:这个数组也是在对应val值的,但是他所对应的val值有可能是自己所对应的val值,也有可能是自己的父亲所对应的val值。
root2:这个数组是在以root1为标准所建的左偏树上对应的val值。
-我们在dfs的时候,先root2左偏树合并,要看一下就是每一个节点是否满足“它的儿子的权值严格小于它本身的权值”
这个条件,如果不满足,那我们就需要统计使他满足所需要的花费。
不满足的时候:
删除掉
r
o
o
t
2
[
x
]
root2[x]
root2[x],将删完后的
r
o
o
t
2
[
x
]
root2[x]
root2[x]和
r
o
o
t
2
[
删
完
前
的
r
o
o
t
2
[
x
]
]
root2[删完前的root2[x]]
root2[删完前的root2[x]]合并。
处理完这些不满足的情况后,我们就开始统计答案。
统计答案:
每次
a
n
s
ans
ans加上相邻两个点满足限制所需要的花费,再更新root1。
代码:
#include<bits/stdc++.h>
using namespace std;
int x,n,size[1000010],to[1000010],nct,nxt[1000010],head[1000010],root[1000010],root1[1000010],root2[1000010];
long long val[1000010],ans;
void adde(int x,int y)
{
to[++nct]=y;
nxt[nct]=head[x];
head[x]=nct;
}
namespace tree1
{
int dis[1000010],cnt,fa[1000010],ch[1000010][2];
int merge(int x,int y)
{
if(!x||!y)
{
return x+y;
}
if(val[x]<val[y])
{
swap(x,y);
}
ch[x][1]=merge(ch[x][1],y);
if(dis[ch[x][0]]<dis[ch[x][1]])
{
swap(ch[x][0],ch[x][1]);
}
dis[x]=dis[ch[x][1]]+1;
return x;
}
int pop(int x)
{
return merge(ch[x][0],ch[x][1]);
}
};
namespace tree2
{
int dis[1000010],cnt,fa[1000010],ch[1000010][2];
int merge(int x,int y)
{
if(!x||!y)
{
return x+y;
}
if(val[root1[x]]<val[root1[y]])
{
swap(x,y);
}
ch[x][1]=merge(ch[x][1],y);
if(dis[ch[x][0]]<dis[ch[x][1]])
{
swap(ch[x][0],ch[x][1]);
}
dis[x]=dis[ch[x][1]]+1;
return x;
}
int pop(int x)
{
return merge(ch[x][0],ch[x][1]);
}
};
void dfs(int x,int d)
{
val[x]+=d;
size[x]=1;
root[x]=root1[x]=x;
for(int i=head[x];i;i=nxt[i])
{
int v=to[i];
dfs(v,d+1);
root2[x]=tree2::merge(root2[x],v);
}
while(val[root1[root2[x]]]>val[root1[x]])//不满足条件
{
int y=root2[x];
root[y]=x;
root2[x]=tree2::pop(y);
root2[x]=tree2::merge(root2[x],root2[y]);
root1[x]=tree1::merge(root1[x],root1[y]);
if(size[x]&&size[y])
{
root1[x]=tree1::pop(root1[x]);
}
size[x]^=size[y];
}
}
void dfs1(int x)
{
ans+=abs(val[x]+0ll-val[root1[root[x]]]);//统计答案
root1[x]=root1[root[x]];
for(int i=head[x];i;i=nxt[i])
{
int v=to[i];
dfs1(v);
}
}
int main()
{
scanf("%d%lld",&n,&val[1]);
val[0]=-0x7f7f7f7f;
for(int i=2;i<=n;i++)
{
scanf("%d%lld",&x,&val[i]);
adde(x,i);//连边
}
dfs(1,0);
dfs1(1);
printf("%lld\n",ans);
return 0;
}