Codeforces Round #530 (Div. 2):D. Sum in the tree (题解)

D. Sum in the tree

题目链接:https://codeforces.com/contest/1099/problem/D

题意:

给出一棵树,以及每个点的si,这里的si代表从i号结点到根节点的权值和。但是有些si=-1,这就相当于丢失了当前结点的数据。

假设原本每个点的权值为ai,那么现在求sum{ai}的最小为多少,ai为非负数。

 

题解:

这题可以单独看每一条链上的s值,假设当前结点为u,儿子结点v,那么就有几种情况:

1.su==-1&&sv==-1,这种不用管,继续往下看;

2.su==-1&&sv>=0,这种情况,su以及上面可能有多个si为-1的结点,但这里我们可以就把它看作一个点,然后找到非-1父亲结点的s值,假设为p,那么sv-sp就是中间结点的权值和,看作一个点的话,就是那个点的a值,同时根据这个我们可以计算出其sum值。至于这里怎么求sp,可以在dfs的时候记录一下。

3.su>=0&&sv==-1,这里就需要上面的记录了,记录目前的su值,方便后面找sp

4.su>=0&&sv>=0,这里直接往下搜索就行了。

结合上面的分析,我们只需要一个dfs记录一下就好了,最后求au的时候就是sumu-sump,也可以在dfs的过程中处理。

但是我们刚才只是对链的分析,一个结点可能有多个儿子结点。想一下,会发现只有上面第2种情况会多考虑一点,因为根据哪个儿子结点来确定当前的s是一个问题。

这个问题也不难解决,取min{sv-sp}即可,一方面是让其尽量大,另一方面是保证方案可行。

最后再判断一下可行性就行了。

代码如下:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5+5;
int n;
vector <int> g[N];
ll sum[N],b[N];
int flag = 0;
void dfs(int u,ll a,int fa){
    int son = 0;
    if(flag) return ;
    int f=0;
    for(auto v:g[u]){
        if(v==fa) continue ;
        if((sum[u]==-1 && sum[v]>=0) ||f){
            f=1;
            ll now=sum[v]-a;
            if(sum[u]==-1) sum[u]=now+a;
            if(sum[u]!=-1) sum[u]=min(sum[u],now+a);
        }
    }
    for(auto v:g[u]){
        if(v==fa) continue ;
        son++;
        if(sum[v]==-1 && sum[u]==-1){
            dfs(v,a,u);
            continue ;
        }
        dfs(v,sum[u],u);
    }
    if(son==0&&sum[u]==-1){
        b[u]=0;
        return ;
    }
    if(sum[u]!=-1) b[u]=sum[u]-a;
}
int main(){
    cin>>n;
    for(int i=2;i<=n;i++){
        int f;
        scanf("%d",&f);
        g[f].push_back(i);
        g[i].push_back(f);
    }
    for(int i=1;i<=n;i++) scanf("%I64d",&sum[i]);
    if(sum[1]==-1) sum[1]=0;
    dfs(1,0,-1);
    for(int i=1;i<=n;i++){
        if(b[i]<0) flag=1;
    }
    if(flag) puts("-1");
    else{
        ll ans = 0;
        for(int i=1;i<=n;i++) ans+=b[i];
        cout<<ans;
    }
    return 0;
}
/*
7
1
2
1
4 4 5
0 -1 3 -1 -1 3 -1
*/

 

转载于:https://www.cnblogs.com/heyuhhh/p/10398307.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值