一句话题意就是求点分树最小深度。
点分树有一个性质:我们称点
i
i
在点分树上距叶子的距离为其权值,那么对于两个点
u,v
u
,
v
满足
wu=wv=k
w
u
=
w
v
=
k
,在原树路径
(u,v)
(
u
,
v
)
上一定存在点
t
t
使得,证明很显然。
我们对每个点
i
i
求出一个二进制状态,二进制第位表示该点子树中存不存在一个
wj=k
w
j
=
k
的点
j
j
,且路径上没有
w>k
w
>
k
的点,因为点分树的深度是
O(log)
O
(
log
)
的,这个二进制状态不会很大。假设我们已知
i
i
所有儿子的二进制状态,那么的取值就受到两个限制:
1. 如果存在两个儿子第
k
k
位同为,那么
wi>k
w
i
>
k
。
2. 如果存在某个儿子第
k
k
位为,那么
wi≠k
w
i
≠
k
。
在满足这两个性质的前提下,
wi
w
i
取合法的最小值(至于为什么取最小的最优不太会证。。。但感觉挺显然的),之后
i
i
点的二进制状态就是其所有儿子状态或和,再把位赋成
1
1
并把一下的位赋为
0
0
<script type="math/tex" id="MathJax-Element-28">0</script>即可。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 100010
using namespace std;
int n,tote,to[N<<1],nxt[N<<1],con[N],w[N],ans;
void ins(int x,int y)
{
to[++tote]=y;
nxt[tote]=con[x];
con[x]=tote;
}
void dfs(int v,int fa)
{
int dep=-1;
bool flag=0;
for(int p=con[v];p;p=nxt[p])
if(to[p]!=fa)
{
dfs(to[p],v);
int tmp=w[v]&w[to[p]];
for(int i=30;i>=0;i--)
if((tmp>>i)&1) {dep=max(dep,i);break;}
w[v]=w[v]|w[to[p]];
flag=1;
}
if(!flag) {w[v]=1;return ;}
for(int i=dep+1;i<=30;i++)
if((w[v]>>i)&1)
dep=max(dep,i);
else break;
ans=max(ans,dep+1);
int R=(1<<(dep+1)),T=((1<<30)-1)^(R-1);
w[v]=(w[v]&T)|R;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
ins(x,y);ins(y,x);
}
dfs(1,0);
printf("%d",ans);
return 0;
}