Loading

【题解】[JOISC 2021 Day3] ビーバーの会合 2

首先 \(j\) 为奇数时答案为 \(1\)

简单证明,\(j\) 个点之间的重心一定是一个可期待点。

根据重心的性质,所有可能的重心一定构成一条链。

那么已知一个重心为 \(x\) ,如果要移动到一个相邻点 \(y\) ,则断开 \((x,y)\) 后两颗子树大小一定相同。

\(j\) 为奇数时两边一定一奇一偶,不可能相等。

同理我们可以推出对于偶数 \(j\) 的答案是最长的链的长度使得链两端的子树大小 \(\ge \dfrac{j}{2}\)

求所有满足条件的链中长度最大的,比较套路的做法是点分治。

我们开一个桶记录后缀最大值即可,更新答案时合并两个桶即可。

一个细节是点分治时单链可以单独作为答案,因为分治时取的是重心,所以每一个子树大小一定 \(\le \dfrac{size}{2}\)

时间复杂度 \(\mathcal{O}(N\log N)\)

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define pre(i,a,b) for(int i=a;i>=b;i--)
#define N 200005
using namespace std;
int n,h[N],tot,v[N],sz[N],u[N],w[N],ed,mn,cs,ans[N];
struct edge{int to,nxt;}e[N<<1];
void add(int x,int y){e[++tot].nxt=h[x];h[x]=tot;e[tot].to=y;}
void find(int x,int fa){
	sz[x]=1;int cur=0;
	for(int i=h[x];i;i=e[i].nxt)if(e[i].to!=fa&&!v[e[i].to])
		find(e[i].to,x),cur=max(cur,sz[e[i].to]),sz[x]+=sz[e[i].to];
	cur=max(cur,cs-sz[x]);
	if(cur<mn)mn=cur,ed=x;
}
void dfs(int x,int fa,int dis){
	sz[x]=1;
	for(int i=h[x];i;i=e[i].nxt)if(e[i].to!=fa&&!v[e[i].to])
		dfs(e[i].to,x,dis+1),sz[x]+=sz[e[i].to];
	w[sz[x]]=max(w[sz[x]],dis);
}
void calc(int x,int wsz){
	cs=mn=wsz;find(x,0);x=ed;
	int ct=0;v[x]=1;
	for(int i=h[x];i;i=e[i].nxt)if(!v[e[i].to]){
		int y=e[i].to;
		dfs(y,x,1);ct=max(ct,sz[y]);
		pre(j,sz[y],1)w[j]=max(w[j],w[j+1]),ans[j*2]=max(ans[j*2],w[j]+u[j]);
		rep(j,1,sz[y])u[j]=max(u[j],w[j]),w[j]=0;
	}
	rep(i,1,ct)u[i]=0;
	for(int i=h[x];i;i=e[i].nxt)if(!v[e[i].to])calc(e[i].to,sz[e[i].to]);
}
int main(){
	scanf("%d",&n);
	rep(i,1,n-1){
		int x,y;scanf("%d%d",&x,&y);
		add(x,y);add(y,x);
	}
	calc(1,n);rep(i,1,n)printf("%d\n",ans[i]+1);
	return 0;
}
posted @ 2021-10-04 17:11  7KByte  阅读(95)  评论(0编辑  收藏  举报