D. Uninity
考虑这棵树形成的过程:初始都为 \(0\),每次合并时变为 \(k\) ,我们则将合并中心打上 \(k\) 的标记。结束时每个点上都有标记,这些标记有性质:对于不同的两点 \(u,v\),若 \(tag_u=tag_v\),则在 \(u,v\) 的路径上存在一点的 \(tag\) 大于它们。可以发现这也是冲要条件。
现在我们需要让最大的 \(tag\) 最小。考虑贪心地从叶子往根(随意钦定)选。记显然每个都选最小可行的可以保证正确性。记 \(s_x\) 集合表示在 \(x\) 的子树中已经存在且到 \(x\) 还没有比它大的集合。
-
\(c\in child_x\),则 \(tag_x\notin s_c\)
-
\(c_1,c_2\in child_x\),则 \(tag_x>\max(s_{c_1}\cap s_{c_2})\)
若不满足 \(1\),则存在其子树中一点到 \(x\) 不满足。
若不满足 \(2\),则存在两个不同子树中的点通过 \(x\) 连起来而不满足。
此时我们已经知道了 \(tag_x\),考虑由子树信息推出 \(s_x\)。我们发现所有小于 \(tag_x\) 的都可取了,所有大于 \(tag_x\) 且原来就不可取的还是不可取,且 \(tag_x\) 不可取。
#include<bits/stdc++.h>
using namespace std;
#define inf 1e9
const int maxn=2e5+10;
const int mod=1e9+7;
inline int read(){
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-'0';c=getchar();}
return x*f;
}
int n,m,ans,lab[maxn],s[maxn];
int beg[maxn],nex[maxn],to[maxn],e;
inline void add(int x,int y){
nex[++e]=beg[x];beg[x]=e;to[e]=y;
nex[++e]=beg[y];beg[y]=e;to[e]=x;
}
inline void dfs(int x,int fa){
int tt=0;
for(int i=beg[x];i;i=nex[i]){
int t=to[i];
if(t==fa)continue;
dfs(t,x);
tt|=s[x]&s[t];
s[x]|=s[t];
}
int k=tt?32-__builtin_clz(unsigned(tt)):0;
lab[x]=__builtin_ctz(unsigned((s[x]|((1<<k)-1))+1));
s[x]=(s[x]>>lab[x]|1)<<lab[x];
}
int main(){
n=read();
for(int i=1,x,y;i<n;i++)
x=read(),y=read(),add(x,y);
dfs(1,0);
for(int i=1;i<=n;i++)
ans=max(ans,lab[i]);
printf("%d\n",ans);
return 0;
}