一句话题意就是求点分树最小深度。
点分树有一个性质:我们称点
在点分树上距叶子的距离为其权值
,那么对于两个点
满足
,在原树路径
上一定存在点
使得
,证明很显然。
我们对每个点
求出一个二进制状态,二进制第
位表示该点子树中存不存在一个
的点
,且
路径上没有
的点,因为点分树的深度是
的,这个二进制状态不会很大。假设我们已知
所有儿子的二进制状态,那么
的取值就受到两个限制:
1. 如果存在两个儿子第
位同为
,那么
。
2. 如果存在某个儿子第
位为
,那么
。
在满足这两个性质的前提下,
取合法的最小值(至于为什么取最小的最优不太会证。。。但感觉挺显然的),之后
点的二进制状态就是其所有儿子状态或和,再把
位赋成
并把
一下的位赋为
即可。
代码:
#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;
}