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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值