没有兄弟的舞会(百度之星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]<i

其中90%的数据满足n≤1000。


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

Source

2018“百度之星”程序设计大赛 - 复赛

题解:

由于我的电脑出现了问题,没办法,有些代码可能打开有点麻烦,所有的题,我都放在博客上,

方便后来看一看吧,可恶的(unbuntu 18.04)。

这个题其实挺简单的,后来发现,这个题目就是贪心即可。

首先我们排序一下。我们只需要标记一下父节点即可。

然而题解说了一句话。。。树形DP也行。我只是在这里挖个坑,

等我学习了树形DP就需要回来这里看一看。

感受:

这次是自己误打误撞第一次进入了百度之星复赛,但是由于各个方面,

当时我做题就是脑子进水了导致爆零了,但是,我清楚,复赛是前50,人家中石油都可以做到。

我们更应该向别人学习,我知道现在我还是雏鸟。我相信自己,把时间多学学新的算法。

下一年的现在我一定要进入前500名。

附上贪心代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+10;
typedef struct node{
    int w,fa;
}node;
void solve(){
    int n;
    scanf("%d",&n);
    node a[N];
    int mp[N]={0};
    a[1].fa=N-1;
    for(int i=2;i<=n;i++){
        scanf("%d",&a[i].fa);
    }
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i].w);
    }
    sort(a+1,a+n+1,[](node x,node y){return x.w>y.w;});
    ll ans1=0,ans2=0;
    int flag1=0,flag2=0;
    for(int i=1;i<=n;i++){
        int fa=a[i].fa,w=a[i].w;
        if(mp[fa]==1&&flag1==0&&w>0){
            flag1=1;
            ans1+=w;
        }else if(mp[fa]==0&&w>0){
            mp[fa]++;
            ans1+=w;
        }else if(w<=0){
            break;
        }
    }
    memset(mp,0,sizeof(mp));
    sort(a+1,a+n+1,[](node x,node y){return x.w<y.w;});
    for(int i=1;i<=n;i++){
        int fa=a[i].fa,w=a[i].w;
        if(mp[fa]==1&&flag2==0&&w<0){
            flag2=1;
            ans2+=w;
        }else if(mp[fa]==0&&w<0){
            mp[fa]++;
            ans2+=w;
        }else if(w>=0){
            break;
        }
    }
    printf("%lld %lld\n",ans1,ans2);
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--){
        solve();
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值