题意:
题目给出一棵树,以结点 (1)为根,树上每个结点都有一个操作码 (flag)。
- (flag = 0),则这个结点的值为其孩子结点的最小值;
- (flag = 1),则这个结点的值为其孩子结点的最大值;
需要给每个叶子结点分配 (1sim k)的值(每个结点的值不重复)。问根结点的最大值为多少。
想法:
显然,对于直接通过某种方式去分配叶子结点的值是无法做到的。
-
考虑树形dp的做法。我们可以考虑怎么求出根结点会受到几个叶子结点的影响,然后把最大的几个值分配给这几个结点即可。答案即是 (k-num+1)( (k=叶子结点数) , (num=影响根结点的叶子数) )。
-
设 $dp[i] $表示以结点 (i) 为根的子树中,结点 (i) 会受到几个叶子结点的影响。那么在转移方程中, 如果(flag[i]=1),(dp[i]=其子树中的dp最小值);如果 (flag[i]=0),(dp[i]=其子树的dp值之和)。
代码:
int dp[maxn];
vector<int>g[maxn];
int n;
int flag[maxn];
void dfs(int x)
{
int maxx=0;
int minx=inf;
for(int i=0;i<g[x].size();i++){
int v=g[x][i];
dfs(v);
minx=min(minx,dp[v]);
maxx+=dp[v];
}
if(g[x].size()==0){
dp[x]=1;
}else{
if(flag[x]==0){
dp[x]=maxx;
}else{
dp[x]=minx;
}
}
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++){
scanf("%d",&flag[i]);
}
for(int i=2;i<=n;i++){
int num;
scanf("%d",&num);
g[num].push_back(i);
}
dfs(1);
int sum=0;
for(int i=1;i<=n;i++){
if(g[i].size()==0)sum++;
}
printf("%d",sum-dp[1]+1);
}