CF1632E2题解

容易发现新加的边一定是 \(1\) 到某个深度大于 \(i\) 的节点。

考虑枚举一颗子树表示 \(b\) 在这颗子树内且这颗子树内通过 \((1,b)\) 走更优。

容易发现答案为 \(\max(x+\lfloor\frac{len+1}{2}\rfloor,t)\)\(x\) 表示加的边的长度,\(len\) 表示当前子树的直径,\(t\) 表示非子树节点最深节点的深度。

那么相当于对 \(n\) 个类似 \(f(x)=\max(x+k,b)\) 的函数取 \(\min\),可以做到 \(O(n)\)

#include<cstdio>
#include<cctype>
const int M=3e5+5;
int T,n,dfe,h[M],f[M],d[M],len[M],mx1[M],mx2[M],md1[M],md2[M],ans[M];
struct Edge{
	int v,nx;
}e[M<<1];
inline void Add(const int&u,const int&v){
	e[++dfe]=(Edge){v,h[u]};h[u]=dfe;
	e[++dfe]=(Edge){u,h[v]};h[v]=dfe;
}
inline int read(){
	int n(0);char s;while(!isdigit(s=getchar()));while(n=n*10+(s^48),isdigit(s=getchar()));return n;
}
inline void write(int n){
	static char s[20];int top(0);while(s[++top]=n%10^48,n/=10);while(putchar(s[top]),--top);
}
inline int max(const int&a,const int&b){
	return a>b?a:b;
}
inline int min(const int&a,const int&b){
	return a>b?b:a;
}
inline void DFS(const int&u){
	for(int v,E=h[u];E;E=e[E].nx)if((v=e[E].v)^f[u]){
		d[v]=d[u]+1;f[v]=u;DFS(v);len[u]=max(len[u],len[v]);
		if(mx1[v]+1>mx1[u])mx2[u]=mx1[u],mx1[u]=mx1[v]+1;else if(mx1[v]+1>mx2[u])mx2[u]=mx1[v]+1;
	}
	len[u]=max(len[u],mx1[u]+mx2[u]);if(u==1)return;
	if(mx1[u]+d[u]>md1[d[u]])md2[d[u]]=md1[d[u]],md1[d[u]]=mx1[u]+d[u];
	else if(mx1[u]+d[u]>md2[d[u]])md2[d[u]]=mx1[u]+d[u];
}
signed main(){
	T=read();
	while(T--){
		n=read();for(int i=1;i<n;++i)Add(read(),read());DFS(1);
		for(int i=0;i<=n;++i)ans[i]=mx1[1];
		for(int u=2;u<=n;++u){
			int k=(len[u]+1)/2,b=max(mx1[u]+d[u]==md1[d[u]]?md2[d[u]]:md1[d[u]],d[u]-1);
			if(k>b)ans[0]=min(ans[0],k);else ans[b-k]=min(ans[b-k],b);
		}
		for(int i=n-1;i>=1;--i)ans[i]=min(ans[i],ans[i+1]);
		for(int i=1;i<=n;++i)write(ans[i]=min(ans[i],ans[i-1]+1)),putchar('\n');putchar('\n');
		for(int i=0;i<=n;++i)h[i]=f[i]=d[i]=len[i]=mx1[i]=mx2[i]=md1[i]=md2[i]=ans[i]=0;
	}
}
posted @ 2023-11-15 14:41  Prean  阅读(14)  评论(0编辑  收藏  举报
var canShowAdsense=function(){return !!0};