Atcoder题解:Abc293_h

首先,考虑小规模的做法。

对于一个位置,如果它的颜色可以继续向上延申,我们称之为打开的,否则就是闭合的。容易发现唯一的闭合的可能是当前的节点颜色和某两个儿子都相同。

那么 fi,j 表示当前子树 i 闭合,从 i 向下走最多遇到 j 种颜色,子树中颜色最多的路径最少有多少种颜色。gi,j 表示当前子树 i 打开,从 i 向下走最多遇到 j 种颜色,子树中颜色最多的路径最少有多少种颜色。

我们考虑 jfi,j 是否具有单调性,但是很明显是没有的。那么我们就必须考虑优化掉一维。我们发现,如果我们只是验证某个 k 是否可行,就只需要记录当前下方已经不能再拓展的的路径颜色个数是否小于等于 k 就可以了。而这个状态和 j 是有单调性的。而且对于大于 k 的答案就可以直接忽略为不可能。

所以我们考虑二分答案 k,每次验证 k 是否可行。这样我们就可以把状态优化到一维。

  • fi 表示闭合或者打开的最小深入颜色

  • gi 表示打开的最小深入颜色

为什么 fi 是“打开或闭合”呢?是为了有性质 figi

对于没有儿子的点,直接建立新颜色,打开。

对于一个儿子的点,枚举是“儿子闭合,建立新颜色”还是“儿子打开,开创新颜色”。

对于两个及以上儿子的点,我们有三种选择:

  1. 把当前节点涂成和所有儿子都不一样的颜色。打开。

  2. 把当前节点涂成和某一个儿子一样的颜色。

  3. 把当前节点作为连接点,连接某两个儿子为同一种颜色。

对于情况 1

令所有儿子中 f 的最大值为 max1f,次大值为 max2f

在满足 max1f+max2f+1k 的条件下,有转移 gi=max1f+1

对于情况 2

我们枚举当前颜色和哪一个儿子一样,设它是 a。然后令 a 以外的儿子中,f 的最大值为 max1f,次大值为 max2f,若只有两个儿子则为 0

max1f+max2f+1k 并且 ga+maxfk 的条件下,有转移 gi=max(max1f+1,ga)

我们可以处理出所有的儿子,按照 fi 排序。枚举 a,取前 3 个,如果有 a 就排除掉,保证至少能选到两个。得到 max1fmax2f

对于情况 3

先考虑暴力转移,尝试枚举当前节点和哪两个儿子一样,设为 a,b(gagb)ab 以外的儿子中,f 的最大值为 max1f,次大值为 max2f,若不足则为 0

则在 max1f+max2f+1kga+gb1kmax1f+gak 三个条件都满足的情况下,有转移 fi=max{max1f,ga)}

我们发现,所有的转移条件中,用到 gb 的只有一个,就是 ga+gb1k,就可以贪心的选取 gb 最小的位置作为 b

则先用前面的手段 O(1) 处理出 a 以外 f 的前三大,然后:

  1. 如果 bmax1f,则转移中的 max1fmax2f 对应处理出的 max2fmax3f,直接转移。

  2. 如果 bmax2f,则转移中的 max1fmax2f 对应处理出的 max1fmax3f,直接转移。

  3. 如果 bmax3f 以及其他的数,转移中的 max1fmax2f 就是处理出的 max1fmax2f。然后我们发现这个转移根本不需要考虑,因为前面的“和某一个儿子相同向上打开”的转移是严格覆盖了这个转移的。转移条件更宽,可以同时转移到 figi,所以根本不需要考虑。

最后还有 fi=min(fi,gi)

然后就可以解决这道题。

#define int long long
int n,a,b,f[200005],g[200005];
vt<int>vv[200005];int res=0,k=0;
inline void dfs(int x,int p){
	f[x]=1e9,g[x]=1e9;vt<pii>vp;
	for(auto j:vv[x])if(j!=p)dfs(j,x);
	for(auto j:vv[x])if(j!=p)vp.pb({f[j],j});
	if(vp.size()==0)return(void)(g[x]=f[x]=1);
	if(vp.size()==1)return(void)(g[x]=min(g[vp[0].second],f[vp[0].second]+1),f[x]=g[x]);
	sort(vp.begin(),vp.end(),[](pii a,pii b){
		return a.first>b.first;
	});
	if(vp[0].first+vp[1].first+1<=k)g[x]=min(g[x],vp[0].first+1);
	for(auto a:vv[x])if(a!=p){
		vt<pii>vs;
		for(int j=0;j<min(3ll,(int)vp.size());j++)if(vp[j].second!=a)vs.pb(vp[j]);
		if(((int)vs.size()<2||vs[0].first+vs[1].first+1<=k)&&g[a]+vs[0].first<=k){
			g[x]=min(g[x],max(vs[0].first+1,g[a]));
		}
	}
	for(auto a:vv[x])if(a!=p){
		vt<pii>vs;
		for(int j=0;j<min(4ll,(int)vp.size());j++)if(vp[j].second!=a)vs.pb(vp[j]);
		if(g[vs[0].second]<=g[a]){
			if((int)vs.size()<3||vs[1].first+vs[2].first+1<=k){
				if((int)vs.size()<2||vs[1].first+g[a]<=k){
					if(g[vs[0].second]+g[a]-1<=k){
						f[x]=min(f[x],max(vs[1].first+1,g[a]));
					}
				}
			}
		}
		if((int)vs.size()>1&&g[vs[1].second]<=g[a]){
			if((int)vs.size()<3||vs[0].first+vs[2].first+1<=k){
				if(vs[0].first+g[a]<=k){
					if(g[vs[1].second]+g[a]-1<=k){
						f[x]=min(f[x],max(vs[0].first+1,g[a]));
					}
				}
			}
		}
	}
	f[x]=min(f[x],g[x]);
}
inline bool valid(int x){
	k=x;
	dfs(1,0);
	return (f[1]<=k);
}
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>n;
	rp(i,n-1){
		cin>>a>>b;
		vv[a].pb(b);
		vv[b].pb(a);
	}
	int l=1,r=n,mid,ans=0;
	while(l<=r){
		mid=(l+r)>>1;
		if(valid(mid))r=mid-1,ans=mid;
		else l=mid+1;
	}cout<<ans<<endl;
	return 0;
}
//Crayan_r
posted @   jucason_xu  阅读(61)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示