题解 CF1632E2 【Distance Tree (hard version)】

· · 题解

首先,连出的边的一个端点必定为 1,因为以其他点为端点不会使答案变的更优。

假如我们已经得到 f(x),需要反推方案,那么只用考虑深度 >f(x) 的节点,因为对深度 \le f(x) 的节点连边没有意义。

dis_{ans} 表示深度 >ans 的节点两两之间的最大距离,则有 ans\ge\lceil \frac{dis_{ans}}{2}\rceil+x

考虑这个不等式的意义,它其实是将深度 >ans 的节点拉出来建了个虚树,然后连了 (1,直径中点) 这条边,这样做显然最优,那么此时的最大深度显然是 \lceil\frac{dis_{ans}}{2}\rceil+x,由于 ans 合法,所以有 ans\ge\lceil\frac{dis_{ans}}{2}\rceil+x

由于 ans 增大会使深度 >ans 的节点数量减少,所以 dis_{ans} 是单调减的,这就可以使用二分 O(n\log n) 计算了。

进一步考虑,x 增大只会使 ans 增大,所以直接 O(n) 计算就完事了。

```cpp #include<bits/stdc++.h> using namespace std; #define ri register int typedef long long ll; const int maxn=3e5+10; template<class T>inline bool ckmin(T &x,const T &y){return x>y?x=y,1:0;} template<class T>inline bool ckmax(T &x,const T &y){return x<y?x=y,1:0;} template<class T>inline void clear(T *arr,int siz,int val=0){memset(arr,val,sizeof(T)*(siz+1));} int dis[maxn]; vector<int>e[maxn]; int dfs(int p,int f,int d){ ri a=0,b=0; for(ri i:e[p]) if(i!=f){ ri c=dfs(i,p,d+1)+1; if(c>a)b=a,a=c; else ckmax(b,c); } if(b+d)ckmax(dis[b+d-1],(a+b+1)>>1); return a; } int n,t_case; int main(){ scanf("%d",&t_case); while(t_case--){ scanf("%d",&n); for(ri i=1;i<=n;++i)dis[i]=0,vector<int>().swap(e[i]); for(ri i=1,x,y;i<n;++i){ scanf("%d%d",&x,&y); e[x].push_back(y),e[y].push_back(x); } ri lim=dfs(1,0,0); for(ri i=n-2;~i;--i)ckmax(dis[i],dis[i+1]); ri ans=1; for(ri i=1;i<=n;++i){ while(dis[ans]+i>ans)++ans; printf("%d%c",min(ans,lim)," \n"[i==n]); } } return 0; } ```