Serval and Rooted Tree
题目大意
给一棵有根树,设有k个叶子节点,给所有的叶子节点标号1~k;然后每个非叶子节点都有一个得到当前节点标号的方法,就是取他的所有子节点的最大值或最小值(取决于输入);问根节点能得到的最大的标号是啥。
刚开始不会做,想着贪心可不可以做,于是想了半天,啥求都不会
于是瞅了一眼题解,dp做,向来不怎么dp的我很是懵逼,还是不知道怎么dp,于是又看了看,如下:
dp[i] 表示第i个节点的标号是他所有子节点标号中的第几大,也就是1~k中比它大的数至少有dp[i] - 1个
于是
叶子结点的dp值就是1。
如果操作是得到子节点的最大值,那么当前的dp值就等于子节点的dp值之和。
如果操作是得到子节点的最大值,那么当前的dp值就等于子节点的dp值的最小值,为什么是最小值?因为要是当前节点的值最大,比当前节点大的数越少越好,所以是最小值。
害~ 把maxn看错还re了一发。
代码:
这个代码其实dfs里不用fa标记的,因为题目给出树的时候直接给的是它的父亲节点是谁,并且是个有根树,所以就建单向边就好,并没有当前节点到父节点的边。
#include<stdio.h>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
const int maxn = 3e5+5;
vector<int> vv[maxn];
int dp[maxn];
int a[maxn];
int s =0 ;
void dfs(int x,int fa)
{
int sum = 0;
int f = 0;
int maxx = maxn;
for(int i = 0; i < vv[x].size(); i ++ )
{
int v = vv[x][i];
if(v == fa)
continue;
f = 1;
dfs(v,x);
sum += dp[v];
maxx = min(maxx, dp[v]);
}
if(f == 0)
{
s ++ ;
dp[x] = 1;
}
else if(a[x] == 1)
{
dp[x] = maxx;
}
else
dp[x] = sum;
// printf("%d %d\n",x,dp[x]);
}
int main()
{
int n;
scanf("%d",&n);
for (int i = 1; i <= n; i ++ )
{
scanf("%d",&a[i]);
}
for (int i = 2; i <= n; i ++ )
{
int x;
scanf("%d",&x);
vv[x].push_back(i);
}
dfs(1,0);
// printf("%d\n",s);
printf("%d\n",s - dp[1] + 1);
}