CCPC HeNan 2022 Problem J. Mex Tree

 题意:有一棵n个节点的树,节点的值是0~n-1的一个排列,要求一棵最大子树T,满足T中未出现的最大值是k,结果分别输出当K是0~n时,T的最大节点数。

思路:注意节点值在哪个节点上是没有关系的,我们只要求节点数,所以可以直接对节点值建树。把树看成父节点为0的一棵从上到下的树,从上至下遍历。当前遍历到now节点,如果在now节点的子树中,最小的节点值是大于now的,则说明now及其所有的子树都不可选,就选now的父节点及父节点的其他子树,所有ans[now] = n - siz[now],如果子树中的最小值小于now,则要选到比now小的节点值一定要选now,因为0在now之上,比now小的值在now之下,则一定要经过now,此时ans[now]=0。对于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;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值