CF1914F Programming Competition

Problem - 1914F - Codeforces

  • 我觉得这个题的难度比 1900 要大吧,感觉细节挺多的

  • 先说一个官方题解的做法

  • 我们从上到下的考虑,对于以 u 为根时我们把 u 的所有儿子的 sizx 记录下来,让不同儿子的 sizx 两两匹配,这里会分成两种情况

    1. 如果 sizmxsizu1sizmx,则我们一定可以把这些合并成 sizu12

    2. 如果 sizmx>sizu1sizmx,那我们在把 mx 和其他所有儿子合并后依然还剩下 sizmx(sizu1sizmx) 个没有合并,而且都在 mx 子树里,这时候我们就递归到 mx 里继续寻找答案

  • 现在的细节就是我们继续寻找答案的时候我们要以什么方式去匹配

  • 答案是优先匹配祖先节点的

  • 考虑如果存在一种最优解是先考虑了 mx 子树内的匹配再去考虑 mx 和祖先的匹配

  • 如果祖先还能继续匹配的个数是偶数,那我们把 mx 的一对匹配拆开匹配给祖先显然等价

  • 如果祖先还能继续匹配的个数为奇数,那我们如果把 mx 的一对匹配拆开后把一个和祖先匹配,另一个剩下,到达这个祖先节点的父节点时两者没有被匹配的节点数是相同的,也就是说两者匹配数相同,依然是最优解

  • 而且我们变换为先匹配祖先节点的原因是先考虑子孙节点比较“片面”,典型的就是第 5 个样例,如果先考虑子孙节点就会出现 (5,6),(2,3) 这样的组合,答案容易更劣;而考虑祖孙节点更“整体”

  • 现在我们既然递归到 mx 里,我们就需要把祖孙节点和他匹配的情况给挑出来。我们在递归进去时记一个 K 表示 mx 的祖先节点已经用掉了 K 个点,因此情况变为:

    1. 如果 sizmxksizu1sizmx,则我们一定可以把这些合并成 sizu12

    2. 如果 sizmxk>sizu1sizmx,那我们在把 mx 和其他所有儿子合并后依然还剩下 sizmx(sizu1sizmx) 个没有合并,而且都在 mx 子树里,这时候答案为 sizu1sizmx+solve(mx,max(k+sizu1siz[mx]1,0))

  • 其中注意的是 mx 子树内是没有节点和 mx 自身匹配的,因此当往下递归时要让 K 减一,不用白不用

  • 最终复杂度为 O(n)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
template<typename T>T &read(T &x){
	cin>>x;
	return x;
}

const int maxn=2e5+50;

int n;
struct E{int v,nxt;}e[maxn];
int hd[maxn],cnt=1;
int siz[maxn];

void ade(int u,int v){
	e[++cnt]=E{v,hd[u]};
	hd[u]=cnt;
}

void dfs1(int u){
	siz[u]=1;
	for(int i=hd[u];i;i=e[i].nxt){
		int v=e[i].v;
		dfs1(v);
		siz[u]+=siz[v];
	}
}

int solve(int u,int k){
	int mx=-1;
	for(int i=hd[u];i;i=e[i].nxt){
		int v=e[i].v;
		if(mx==-1||siz[v]>siz[mx]){
			mx=v;
		}
	}
	if(siz[u]==1){
		return 0;
	}
	if(siz[mx]-k<=siz[u]-1-siz[mx]){
		return (siz[u]-1-k)/2;
	}
	return siz[u]-1-siz[mx]+solve(mx,max(k+siz[u]-1-siz[mx]-1,0));
}

void mian(int TwT){
	read(n);
	int fa;
	for(int i=2;i<=n;++i){
		read(fa);
		ade(fa,i);
	}
	dfs1(1);
	printf("%d\n",solve(1,0));
}

void init(){
	for(int i=1;i<=n;++i){
		hd[i]=0;
	}
	cnt=1;
}

int main(){
	
	int T=1;
	read(T);
	for(int TwT=1;TwT<=T;++TwT){
		init();
		mian(TwT);
	}
	
	return 0;
}

  • 还有洛谷大佬的第二种方法

  • 由于要求 (u,v) 中 u 和 v 必须不互为祖先,容易想到贪心只要选两个叶子节点就行了。

  • 因为如果 (u,v) 不互为祖先,那么选一个 u 的子节点 u0​,(u0,v) 满足条件且更优。

  • 会发现,取两个深度最大的节点时,能保证这颗树剩下的节点中深度尽量平衡,也就是能构成祖先的 (u,v) 会更少,故贪心可以那么做。

  • 复杂度会多优先队列带来的一个 log

posted @   FOX_konata  阅读(17)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
点击右上角即可分享
微信分享提示