【XSY2322】 Ascending Tree(左偏树)

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值