这道题题目有点绕,实际就是找出一种方案,使得从根节点出发的路径的最大值最小,答案即该最大值的最小值。
这道题的先进滑道是可以贪心建造的。
设为从出发往下的路径在最优情况下的最大值。
对于一个节点,假设它的儿子的子树全部都已经为最优方案,那么为了使出去的路径的最大值最小,对于所有的儿子,一定要把先进滑道通向最大的儿子,否则最大的儿子传到的值一定比当前大。
因此一个节点的先进滑道一定是连向dp值最大的儿子。
有了上一步的贪心,可以发现,求每个节点的dp值,必须要所有它的儿子的dp值,因此用树形dp求解。
由于一条全是先进滑道的链(以下称作先进链)的权值只能通过该先进链的长度求取,并不能拆成几段分开求解。因此从一个点往下的所有先进链上的点都要存下来。
由于每个点只会向一个儿子连出一条先进边,根据启发式合并的思想,应该把父亲加到儿子的中,再把父亲指向儿子的。这样每个点加一条先进边复杂度是的。
但是,如果直接把中的所有点都把父亲更新一遍,显然会超时。
考虑。
在时,只有为二的幂或零时,才会+1。
因此,我们只需在把离儿子节点距离为和的位置的值重新更新给父亲,其它都沿用儿子的dp值即可。
这样每个点都只从个点更新。
总复杂度为。
#include<bits/stdc++.h>#define M 100005using namespace std;void Max(int &a,int b){b>a&&(a=b);}int hd[M],nxt[M+M],to[M+M],cnte;void addedge(int a,int b){nxt[++cnte]=hd[a];to[cnte]=b;hd[a]=cnte;}int n;int p[M],dp[M];vector<int> S[M];void dfs(int x,int f){int Son=0,mx=-1;for(int i=hd[x];i;i=nxt[i]){int y=to[i];if(y==f)continue;dfs(y,x);if(dp[y]>dp[Son])Son=y;}for(int i=hd[x];i;i=nxt[i]){int y=to[i];if(y==f||y==Son)continue;Max(mx,dp[y]);}if(!Son){S[x].push_back(0);dp[x]=0;}else {p[x]=p[Son];dp[x]=max(mx+1,dp[Son]);int sz=S[p[x]].size();Max(dp[x],S[p[x]][sz-1]+1);//从零更新for(int i=0;(1<<i)<sz;i++)Max(dp[x],S[p[x]][sz-1-(1<<i)]+i+2);//从二的幂更新S[p[x]].push_back(mx+1);}}void init(){memset(dp,0,sizeof(dp));dp[0]=-1;cnte=0;for(int i=1;i<=n;i++){S[i].clear();hd[i]=dp[i]=0;p[i]=i;}}void solve(){scanf("%d",&n);init();for(int i=1;i<n;i++){int a,b;scanf("%d%d",&a,&b);addedge(a,b);addedge(b,a);}dfs(1,0);printf("%d\n",dp[1]);}int main(){int T;scanf("%d",&T);while(T--)solve();return 0;}