1. 程式人生 > >HDU6409 沒有兄弟的舞會(2018百度之星複賽,貪心)

HDU6409 沒有兄弟的舞會(2018百度之星複賽,貪心)

Problem Description

度度熊、光羽、帶勁三個人是好朋友。
度度熊有一棵n個點的有根樹,其中1號點為樹根。除根節點之外,每個點都有父節點,記i號點的父節點為fa[i]。
度度熊稱點i和點j是兄弟(其中i≠j)當且僅當fa[i]=fa[j]。
第i個點的權值為Ai。現要求選出一個點集,該點集合法當且僅當點集中至多隻有一對兄弟
度度熊想知道,在所有可行的點集中,權值和最大以及最小的點集權值和分別是多少?

Input

第一行一個數,表示資料組數T。
每組資料第一行一個整數n;第二行n−1個數,表示fa[2],fa[3],..,fa[n];第三行n個數,表示Ai。
資料組數T=100,滿足:
- 1≤n≤105
- −109≤Ai≤109
- 1≤fa[i]

Output

每組資料輸出一行,每行包含兩個數,分別表示權值和的最大值最小值

Sample Input

2
5
1 1 2 2 
-4 -4 -1 -2 -5 
5
1 1 3 2 
-1 -4 2 0 -2 

Sample Output

0 -15
2 -7

思路

考慮貪心,我們先利用set把每一個點的權值插進他的父親節點,然後遍歷set

我們很容易知道每一個點的兒子的最大值和最小值,分別在set中的--s[i].end()s[i].begin()的位置。最大值和最小值是一定要選的,其次再維護一個次大值和次小值,最後在答案裡面加上即可。

因為是要求最大值和最小值,所以更新的條件是最大值大於0和最小值小於0

程式碼

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define mem(a, b) memset(a, b, sizeof(a))
const ll N = 1e5 + 100;
ll fa[N], a[N];
set<ll> s[N];
void solve()
{
    ll n, x;
    scanf("%lld", &n);
    for (ll i = 0; i <= n; i++)
    {
        s[i].clear();
        fa[i] = 0
; } for (ll i = 2; i <= n; i++) { scanf("%lld", &x); fa[i] = x; } for (ll i = 1; i <= n; i++) { scanf("%lld", &a[i]); s[fa[i]].insert(a[i]); } ll ans1 = 0, ans2 = 0; ll maxx = 0, minn = 0; for (ll i = 0; i <= n; i++) { if (s[i].size() == 1) { auto it = s[i].begin(); if (*it > 0) ans1 += *it; if (*it < 0) ans2 += *it; } else if (s[i].size() >= 2) { auto it = --s[i].end(); if (*it > 0) ans1 += *it; it--; if (*it > 0) maxx = max(maxx, *it); it = s[i].begin(); if (*it < 0) ans2 += *it; it++; if (*it < 0) minn = min(minn, *it); } } ans1 += maxx, ans2 += minn; printf("%lld %lld\n", ans1, ans2); } int main() { //freopen("in.txt", "r", stdin); ll t; scanf("%lld", &t); while (t--) solve(); return 0; }