CF1632E1/E2题解

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 dudv,那么我们连边 ( 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)+xans。我们发现,这个值 a n s − x ans-x ansx u u u 具体是哪一个点没有关系。所以我们只需要让 max ⁡ ( d i s ( v , u ) ) \max(dis(v,u)) max(dis(v,u)) 尽量小即可。这个模型很经典

我们需要在原树上找到一条最长的路径 x → ⋯ → y x \rightarrow \dots \rightarrow y xy,满足 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+1ans 成立,原因是我们只需要把 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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值