1. 程式人生 > >Codeforces Round #530 (Div. 2):D. Sum in the tree (題解)

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

continue lan 代碼 裏的 解決 ron turn contest pro

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 */

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