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
思路:这道题最容易想到的就是贪心,最大值时只取大于0的权值,最小值只去小于0的权值。等我学会了树形DP再回来优化..
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
typedef long long ll;
const int N = 1e5;
struct node {
int w, fa;
};
node a[N];
//对结构体排序
bool cmp1(node x, node y)
{
if (x.w > y.w)
return true;
else
return false;
}
bool cmp2(node x, node y)
{
if (x.w < y.w)
return true;
else
return false;
}
void solve()
{
int n;
cin >> n;
int mp[N] = { 0 };
a[1].fa = N - 1;
for (int i = 2; i <= n; i++)
{
cin >> a[i].fa;
}
for (int i = 1; i <= n; i++)
{
cin >> a[i].w;
}
sort(a + 1, a + n + 1, cmp1);
ll sum1 = 0, sum2 = 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)//flag用来标志至多只有一对兄弟的要求
{
flag1 = 1;
sum1 += w;
}
else if (mp[fa] == 0 && w>0)
{
mp[fa]++;
sum1 += w;
}
else if (w <= 0)
{
break;
}
}
memset(mp, 0, sizeof(mp));
sort(a + 1, a + n + 1,cmp2);
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;
sum2 += w;
}
else if (mp[fa] == 0 && w<0)
{
mp[fa]++;
sum2 += w;
}
else if (w >= 0)
{
break;
}
}
cout << sum1 << " " << sum2 << endl;
}
int main()
{
int T;
cin >> T;
while (T--)
{
solve();
}
return 0;
}
,