[XSY4197] Snow(树形DP)

24 篇文章 0 订阅

我们在树上的每个点 i i i上放 a i a_i ai小点,初始时先让每个点单独减,这样要花费 a i a_i ai之和的次数。

然后尝试把某些减合并。一个点上面的小点至多可以向两个相邻的小点连边(这两个小点不能在同一个点上)。每连一条边,合并次数+1,答案-1。

问题变成求一棵树内的最大合并次数。

首先明确该问题满足最优子结构,即考虑以 U U U为根的子树时,若 U , V U,V U,V上的小点 u , v u,v u,v可以合并,合并 u , v u,v u,v 一定不比 不合并 u , v u,v u,v以让 f a u , u fa_u,u fau,u合并 劣。
在这里插入图片描述
感性证明: f a u fa_u fau可能可以和不是 u u u的其它小点合并,即使找不到其它可以合并的小点,前面的方案也不必后面的方案劣。

g i g_i gi表示考虑完以 i i i为根的子树,在合并次数最多的情况下 i i i上最多有几个小点能和 i i i的父亲上的小点合并。转移讨论一下即可。
在这里插入图片描述

#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
const int N=2e5+10;
struct Edge{int v,nxt;}e[N<<1];
int cnt,head[N],n;
ll ans,a[N];
void adde(int u,int v){
    e[++cnt].v=v;e[cnt].nxt=head[u];head[u]=cnt;
}
void dfs(int u,int fa){
    ll sum=0,maxn=0,pr;
    for(int i=head[u];i;i=e[i].nxt){
        int v=e[i].v;
        if(v==fa)continue;
        dfs(v,u);
        sum+=a[v],maxn=max(maxn,a[v]);
    }
    if(sum-maxn>=maxn) pr=sum/2;
    else pr=sum-maxn;
    if(sum<=a[u]) ans-=sum;
    else{
        pr=min(pr,min(sum-a[u],a[u]));
        ans-=a[u]+pr;
        a[u]-=pr;
    }
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%lld",&a[i]);ans+=a[i];
    }
    for(int i=1;i<n;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        adde(u,v);adde(v,u);
    }
    dfs(1,0);
    printf("%lld\n",ans);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值