题意
题解
我们考虑考虑一个一个点考虑
那么显然,肯定是把最大的联通块分一点到最小的联通块里面
扫一下就可以知道最大和最小了
当然,答案要和次大的子树取max
那么问题就是分多少,怎么分
显然地,如果两个相差是x
那么分的方式x/2显然是最优的
如果不存在的话那么就去最近的两个
那么我们就需要维护这个东西了
我们需要维护三个set
这个点重儿子里面的子树大小
这个点到根的路径的子树大小
剩下的全部
对于第三个,我们可以用删除的方式,然后剩下两个都是每一次加进来
这个显然可以用dsu来优化,然后就可以了
注意的是,最大子树要是父亲的联通块时要分开考虑
CODE:(打着打着就打长了)
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<set>
using namespace std;
const int N=100005*2;
int n;
int rt;
struct qq
{
int x,y,last;
}e[N];int num,last[N];
void init (int x,int y)
{
num++;
e[num].x=x;e[num].y=y;
e[num].last=last[x];
last[x]=num;
}
int tot[N];
int son[N];
multiset<int> s,s1,s2;//s:不是这个点到根以及这个点子树里面的 s1:子树里面的 s2:这个点到根里面的
multiset<int>::iterator it;
void dfs (int x,int fa)
{
tot[x]=1;
for (int u=last[x];u!=-1;u=e[u].last)
{
int y=e[u].y;
if (y==fa) continue;
dfs(y,x);
tot[x]=tot[x]+tot[y];
if (tot[y]>tot[son[x]]) son[x]=y;
}
s.insert(tot[x]);
}
int ans[N];
void del (int x,int fa)//把这个从子树里面删除
{
it=s1.lower_bound(tot[x]);s1.erase(it);
s.insert(tot[x]);
for (int u=last[x];u!=-1;u=e[u].last)
{
int y=e[u].y;
if (y==fa) continue;
del(y,x);
}
}
void Ins (int x,int fa)
{
it=s.lower_bound(tot[x]);s.erase(it);
s1.insert(tot[x]);
for (int u=last[x];u!=-1;u=e[u].last)
{
int y=e[u].y;
if (y==fa) continue;
Ins(y,x);
}
}
void print ()
{
printf("OZY:\n");
for (it=s.begin();it!=s.end();it++)
{
printf("%d ",(*it));
}
printf("\n");
}
void dfs1 (int x,int fa)
{
s2.insert(tot[x]);
it=s.lower_bound(tot[x]);s.erase(it);
for (int u=last[x];u!=-1;u=e[u].last)
{
int y=e[u].y;
if (y==fa) continue;
if (y==son[x]) continue;
dfs1(y,x);del(y,x);
}
if (son[x]!=0) dfs1(son[x],x);
else
{
ans[x]=n-1;s1.insert(tot[x]);
it=s2.lower_bound(tot[x]);s2.erase(it);
return ;
}
int o=0,id,mn=tot[son[x]];
int mx=tot[son[x]];
for (int u=last[x];u!=-1;u=e[u].last)
{
int y=e[u].y;
if (y==fa) continue;
if (y==son[x]) continue;
/*if (x==1)
{
printf("hehe:%d %d\n",tot[y],mn);
}*/
if (tot[y]>o) o=tot[y];
mn=min(mn,tot[y]);
}
//if (x==1)printf("TYB:%d %d\n",x,mn);
if (fa!=0) mn=min(mn,n-tot[x]);
if (mx<n-tot[x])//问题是父亲
{
for (int u=last[x];u!=-1;u=e[u].last)
{
int y=e[u].y;
if (y==son[x]||y==fa) continue;
Ins(y,x);
}
// printf("x:%d %d\n",x,o);
if (n-tot[x]==tot[son[x]])//如果两个一样
{
ans[x]=mx;
}
else
{
o=mx;
mx=n-tot[x];
int r=(mx-mn)/2;//最多拿出多少个
/* if (x==2)
{
printf("YES:%d %d %d %d\n",x,mn,mx,r);print();
}*/
int lalal=0;
int g=n;
it=s.lower_bound(r+1);
if (it!=s.end()) g=min(g,max(mx-(*it),mn+(*it)));
//printf("first:%d\n",(*it));
if (it!=s.begin())
{
it--;
g=min(g,max(mx-(*it),mn+(*it)));
}
it=s2.lower_bound(r+1+tot[x]);
if (it!=s2.end()) g=min(g,max(mx-((*it)-tot[x]),mn+((*it)-tot[x])));
if (it!=s2.begin())
{
it--;
g=min(g,max(mx-((*it)-tot[x]),mn+((*it)-tot[x])));
}
// printf("ans:%d\n",g);
ans[x]=max(o,g);
}
}
else if (o==0&&fa==0)
{
ans[x]=n-1;
}
else//只需要把重搞走就可以了
{
o=max(o,n-tot[x]);
if (o==mx)
{
ans[x]=mx;
}
else
{
int r=(mx-mn)/2;
int lalal=0;
it=s1.lower_bound(r+1);
/*if (x==1)
{
printf("YES:%d mn:%d mx:%d r:%d %d\n",x,mn,mx,r,*it);
//print();
}*/
//printf("YES:%d %d %d %d\n",x,*(--it),mn,fa);
int g=n;
//printf("lalal:%d\n",*it);
if (it!=s1.end()) g=min(g,max(mn+(*it),mx-(*it)));
if (it!=s1.begin())
{
it--;
g=min(g,max(mn+(*it),mx-(*it)));
}
ans[x]=max(o,g);
}
for (int u=last[x];u!=-1;u=e[u].last)
{
int y=e[u].y;
if (y==son[x]||y==fa) continue;
Ins(y,x);
}
}
s1.insert(tot[x]);
it=s2.lower_bound(tot[x]);s2.erase(it);
return ;
}
int main()
{
num=0;memset(last,-1,sizeof(last));
scanf("%d",&n);
for (int u=1;u<=n;u++)
{
int x,y;
scanf("%d%d",&x,&y);
if (x==0) rt=y;
else {init(x,y);init(y,x);}
}
dfs(rt,0);
dfs1(rt,0);
// printf("ans:%d\n",ans[4]);
for (int u=1;u<=n;u++)
printf("%d\n",ans[u]);
return 0;
}