题意:有一棵n个节点的树,节点的值是0~n-1的一个排列,要求一棵最大子树T,满足T中未出现的最大值是k,结果分别输出当K是0~n时,T的最大节点数。
思路:注意节点值在哪个节点上是没有关系的,我们只要求节点数,所以可以直接对节点值建树。把树看成父节点为0的一棵从上到下的树,从上至下遍历。当前遍历到now节点,如果在now节点的子树中,最小的节点值是大于now的,则说明now及其所有的子树都不可选,就选now的父节点及父节点的其他子树,所有,如果子树中的最小值小于now,则要选到比now小的节点值一定要选now,因为0在now之上,比now小的值在now之下,则一定要经过now,此时
。对于0节点,答案就是其最大子树的大小,对于K=n,答案就是n.
#include<iostream>
using namespace std;
const int N = 1e6 + 10;
const int M = 2 * N;
typedef long long LL;
int n;
int val[N];
int minn[N];//当前节点的子树中的最小值
int siz[N];//当前节点的子树的大小(包括自己)
int ans[N];
int idx, h[N], e[M], ne[M];
void add(int a, int b){
e[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
}
void dfs(int now, int fa){
siz[now] = 1;//now所有子树及自身的大小
minn[now] = 1e6 + 10;//now的子树中编号(节点值)最小的节点
for(int i=h[now]; ~i; i=ne[i]){
int u = e[i];
if(u == fa) continue;
dfs(u, now);
siz[now] += siz[u];
minn[now] = min(minn[now], minn[u]);
}
if(minn[now] > now){//子树里的最小值都比父节点大,那么除了子树以外的全都可以选
ans[now] = n - siz[now];
}//子树里的最小值比父节点小,那么想选到最小值的时候一定要选到now节点,则ans=0
minn[now] = min(minn[now], now);//子树判断结束后,求now这棵树的最小值
return;
}
int main(){
int tem;
scanf("%d", &n);//输入比较多,要用scanf
for(int i=0; i<=n; i++) h[i] = -1;
for(int i=1; i<=n; i++) scanf("%d", &val[i]);
for(int i=2; i<=n; i++){
scanf("%d", &tem);
add(val[tem], val[i]);//直接对节点权值建树
add(val[i], val[tem]);
}
dfs(0, -1);//把节点值为0的节点看做父节点,遍历从0开始的子树
for(int i=h[0]; ~i; i=ne[i]){
ans[0] = max(ans[0], siz[e[i]]);
}
ans[n] = n;
for(int i=0; i<=n; i++){
cout<<ans[i]<<' ';
}
return 0;
}