#include <bits/stdc++.h>
#define F(i,l,r) for(int i=(l);i<=(r);i++)
#define FF(i,r,l) for(int i=(r);i>=(l);i--)
#define ll long long
#define M 200010
using namespace std;
int n;
struct edge{
int v,to;
}e[M];int head[M],tnt;
void add_edge(int x,int y){
e[++tnt].v=y;e[tnt].to=head[x];head[x]=tnt;
}
int ans=2147483647;
int siz[M],hson[M],hpos[M];
int fa[M];
int gp[M],dp[M];
int maxd[M],mint[M];
int op;
void dfs(int u,int f){
fa[u]=f;
for(int i=head[u];i;i=e[i].to){
int v=e[i].v;if(v==f)continue;
dfs(v,u);siz[u]+=siz[v];
if(hson[u]<siz[v]){
hson[u]=siz[v];hpos[u]=v;
}
}
siz[u]++;if(siz[u]==1){dp[u]=u;return ;}
int v=hpos[u];
while(dp[v]!=u){
int k=siz[dp[v]];
if(abs(siz[u]-2*k)<gp[u]){
maxd[u]=max(siz[u]-k,k);
mint[u]=min(siz[u]-k,k);
gp[u]=maxd[u]-mint[u];
dp[u]=dp[v];
}
if(k>siz[u]/2)break;
else dp[v]=fa[dp[v]];
}
}
void dfs1(int u){
int p=siz[1]-siz[u];
int m1=max(maxd[u],p)-min(mint[u],p);
ans=min(ans,m1);
if(!hpos[u])return ;
dfs1(hpos[u]);
}
int main(){
ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);
cin>>n;memset(gp,0x3f,sizeof(gp));
F(i,1,n-1){
int x,y;cin>>x>>y;add_edge(x,y);add_edge(y,x);
}dfs(1,1);
dfs1(1);
cout<<ans<<'\n';
return 0;
}
(以下是我赛时想法) 我的策略是:先切一刀,考虑第二刀
假设第二刀是随便切的,那么令切下来的两个连通块为大小m1,m2(m1<m2);再令第一刀切下来的的部分大小为m。令m为已知量。
若m<m1 则min=m,max=m2,那么要让答案最小,就要缩小m2
若m1<m<m2,则min=m1,max=m2,同理,要缩小m2,增大m1
若m>m2,则min=m1,max=m,同理,要缩小m2,增大m1
综上,对于第二刀切的任意子树,都要使m1,m2尽量接近,故最佳状况为m1=m2;
所以显然第二刀切在该子树的重儿子上
所以有一种做法,就是对每棵子树枚举他重儿子的切点,期望nlgn
但是遇到极限数据(链)会被卡掉
又因为对于每个子树,其切点必然在其重儿子之上,所以我们可以对每个子树的切点进行转移
每次将重儿子的切点向上移,计算并将其最优解切点位置转移,最后再dfs一下统计答案即可
但是假了呢,肿么回事能