Atcoder题解:Abc293_h
首先,考虑小规模的做法。
对于一个位置,如果它的颜色可以继续向上延申,我们称之为打开的,否则就是闭合的。容易发现唯一的闭合的可能是当前的节点颜色和某两个儿子都相同。
那么
我们考虑
所以我们考虑二分答案
-
表示闭合或者打开的最小深入颜色 -
表示打开的最小深入颜色
为什么
对于没有儿子的点,直接建立新颜色,打开。
对于一个儿子的点,枚举是“儿子闭合,建立新颜色”还是“儿子打开,开创新颜色”。
对于两个及以上儿子的点,我们有三种选择:
-
把当前节点涂成和所有儿子都不一样的颜色。打开。
-
把当前节点涂成和某一个儿子一样的颜色。
-
把当前节点作为连接点,连接某两个儿子为同一种颜色。
对于情况
令所有儿子中
在满足
对于情况
我们枚举当前颜色和哪一个儿子一样,设它是
在
我们可以处理出所有的儿子,按照
对于情况
先考虑暴力转移,尝试枚举当前节点和哪两个儿子一样,设为
则在
我们发现,所有的转移条件中,用到
则先用前面的手段
-
如果
是 ,则转移中的 和 对应处理出的 和 ,直接转移。 -
如果
是 ,则转移中的 和 对应处理出的 和 ,直接转移。 -
如果
是 以及其他的数,转移中的 和 就是处理出的 和 。然后我们发现这个转移根本不需要考虑,因为前面的“和某一个儿子相同向上打开”的转移是严格覆盖了这个转移的。转移条件更宽,可以同时转移到 和 ,所以根本不需要考虑。
最后还有
然后就可以解决这道题。
#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
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具