CF1632E1 Distance Tree (easy version)
CF1632E2 Distance Tree (hard version)
很神仙的一道题,结论也很神仙
算法分析
先考虑对于每一个 x x x 如何来求答案。问题可以转化为我们添加的边至少有一个端点是 1 1 1,因为如果添加的边是 ( u , v ) (u,v) (u,v),假设 d u ≤ d v d_u \leq d_v du≤dv,那么我们连边 ( 1 , v ) (1,v) (1,v) 的图一定更优。这个能感性理解。
那么考虑把问题转化,对于一个值 a n s ans ans,是否存在一个点 v v v,使得加入边 ( 1 , v ) (1,v) (1,v) 之后的新图的 max ( d i ) \max(d_i) max(di) 的最小值不大于 a n s ans ans。那么原树里每一个 d u > a n s d_u > ans du>ans 的 u u u 都必须要被新加入的边 ( 1 , v ) (1,v) (1,v) 更新,具体的, d u ′ = min ( d u , x + d i s ( v , u ) ) d'_u = \min(d_u,x+dis(v,u)) du′=min(du,x+dis(v,u)),其中 d i s ( v , u ) dis(v,u) dis(v,u) 为原树中 v v v 到 u u u 的距离。那么对于所有 d u > a n s d_u > ans du>ans 的 u u u,必须满足 d i s ( v , u ) + x ≤ a n s dis(v,u)+x \leq ans dis(v,u)+x≤ans。我们发现,这个值 a n s − x ans-x ans−x 与 u u u 具体是哪一个点没有关系。所以我们只需要让 max ( d i s ( v , u ) ) \max(dis(v,u)) max(dis(v,u)) 尽量小即可。这个模型很经典。
我们需要在原树上找到一条最长的路径 x → ⋯ → y x \rightarrow \dots \rightarrow y x→⋯→y,满足 d x , d y > a n s d_x,d_y > ans dx,dy>ans,设这条路径的长度是 l e n len len,那么答案 a n s ans ans 可行,当且仅当右式 x + ⌊ l e n + 1 2 ⌋ ≤ a n s x + \left\lfloor\dfrac{len+1}{2}\right\rfloor \leq ans x+⌊2len+1⌋≤ans 成立,原因是我们只需要把 v v v 放到这条路径的中点上。
最后就变成了,对于每一个固定的 a n s ans ans,如何求 l e n len len 的值。这里可以用树形DP,我们先假设以 1 1 1 为根,把每一个点到 1 1 1 的距离算出来,然后取一个距离最大的点 r t rt rt,进行第二次 d f s dfs dfs,把每一个点到 r t rt rt 的距离算出来。然后我们更新每一个 d e p dep dep 对应的 f f f 数组表示上面的 l e n len len,先正着取一次 max \max max,再倒着取一次 max \max max。
rep(i,1,n) f[dep[0][i]] = max(f[dep[0][i]],dep[1][i]); //正着先更新一次
drep(i,n-1,0) f[i] = max(f[i],f[i+1]); //反着更新。因为i(ans)变小,答案会变大,想一下就知道,因为
//如果ans变小的话,可供选择的点就多了,答案一定不会更小
最后我们进行二分,看能取到最大的 k k k 是哪个,每次 c h e c k check check 的时候,按上面那个式子进行 c h e c k check check。
总代码
#include <bits/stdc++.h>
#define re register
#define ll long long
#define rep(a,b,c) for(re int a(b) ; a<=c ; ++a)
#define drep(a,b,c) for(re int a(b) ; a>=c ; --a)
using namespace std;
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch == '-') f=-1 ; ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48) ; ch=getchar();}
return x*f;
}
const int M = 3e5+10;
int T,n,rt,maxn;
vector<int> g[M];
int dep[2][M],f[M];
inline void dfs(int u,int fa,int zt){
if(fa) dep[zt][u] = dep[zt][fa] + 1;
for(auto v : g[u]) if(v != fa) dfs(v,u,zt);
}
inline bool check(int mid,int x) { return mid >= min(dep[0][rt],x+(f[mid+1]+1)/2); }
inline void work(){
n = read();
rep(i,0,n) g[i].clear(),f[i] = dep[0][i] = dep[1][i] = 0;
rep(i,1,n-1){
int u = read(),v = read();
g[u].push_back(v),g[v].push_back(u);
}
dfs(1,0,0);
rt = 0,maxn = 0;
rep(i,1,n) if(dep[0][i] > maxn) { maxn = dep[0][i]; rt = i; }
dfs(rt,0,1);
rep(i,1,n) f[dep[0][i]] = max(f[dep[0][i]],dep[1][i]);
drep(i,n-1,0) f[i] = max(f[i],f[i+1]);
rep(x,1,n){
int l = 0,r = n,ans = n;
while(l <= r){
int mid = (l+r)>>1;
if(check(mid,x)) ans = mid,r = mid-1;
else l = mid+1;
}
printf("%d ",ans);
}
puts("");
}
signed main(){
T = read();
while(T--) work();
return 0;
}