题目描述:给出一棵$n$个点的树,树根为$1$,对于每个点$x$求:将这个点$x$删除后,得到许多联通块,改变某一节点的父节点(改变点不能是这个点$x$的子节点也不能是树根),使最大联通块最小,求这个最大联通块的大小。$nle 10^5$
我们考虑对于一个点$x$的答案怎么求:显然是将最大的联通块中取出一个子树接到最小的联通块中。
也就是说联通块大小改变的联通块只有最大联通块与最小联通块。
那么不论最大联通块减小后是否会比次大联通块小,次大联通块的大小都不会改变。
最优决策就一定是使最大联通块的大小与最小联通块的大小都接近他们的平均值(设为$w$)。
现在我们讨论一下最大联通块的情况:
1、维护最大联通块为重儿子,显然我们需要维护这个点重儿子中所有点的子树大小然后找$w$的前驱和后继,比较一下取最优即可。
2、维护最大联通块为根节点所在联通块,改变点在这个点到根的链上,显然这条链上点的子树大小要减掉$x$的子树大小,所以我们单独维护这条链上点的子树大小,查找时查找$w+size[x]$的前驱和后继即可。
3、维护最大联通块为根节点所在联通块,改变点不在这个点到根的链上,我们需要维护除$x$子树中点及$x$到根的链上的点之外的所有点的子树大小,然后找$w$的前驱和后继即可。
对于维护每个点子树大小,需要选择一种数据结构支持插入、删除、求前驱后继,随便用一个平衡树即可,为了好写,我用了$multiset$。
不要忘记次大联通块可能成为最大联通块!
最后再说一下如何维护这三个$set$:
1、$x$到根的链上的点的子树大小的$set$:只需要在$dfs$时每遍历到一个点将它的$size$插入,遍历完删除即可。
2、$x$的重儿子的子树中的点的子树大小的$set$:如果当前点是父节点的重儿子则在遍历结束时插入当前点的子树大小,否则清空$set$。
3、剩余点的子树大小的$set$:显然每遍历到一个点就删除对应的子树大小,对于$x$子树中的点,我们可以树上启发式合并:每到一个点先遍历轻儿子,求出轻儿子子树中每个点的答案,并不将轻儿子中点的子树大小从$set3$中删除(即将遍历轻儿子时删除的轻儿子子树中的点的信息再加回来),然后递归重儿子并保存重儿子子树中的删除,然后再删除轻儿子子树中点的信息。即正常的$dsu on tree$是先递归轻儿子不保存信息再递归重儿子保存信息然后再递归轻儿子保存信息,而本题是先递归轻儿子不删除信息再递归重儿子删除信息然后再递归轻儿子删除信息。
#include<set> #include<map> #include<queue> #include<stack> #include<cmath> #include<cstdio> #include<vector> #include<bitset> #include<cstring> #include<iostream> #include<algorithm> using namespace std; multiset<int>all; multiset<int>chain; multiset<int>sub; multiset<int>::iterator it; int size[100010]; int head[100010]; int to[100010]; int next[100010]; int tot; int x,n; int ans[100010]; int son[100010]; void add(int x,int y) { next[++tot]=head[x]; head[x]=tot; to[tot]=y; } void dfs(int x) { size[x]=1; for(int i=head[x];i;i=next[i]) { dfs(to[i]); size[x]+=size[to[i]]; if(size[to[i]]>size[son[x]]) { son[x]=to[i]; } } all.insert(size[x]); } void calc(int x,int opt) { if(!opt) { all.insert(size[x]); } else if(opt==1) { it=all.lower_bound(size[x]); all.erase(it); } else if(opt==2) { sub.insert(size[x]); } else if(opt==3) { it=sub.lower_bound(size[x]); sub.erase(it); } for(int i=head[x];i;i=next[i]) { calc(to[i],opt); } } void dsu_on_tree(int x,int opt) { it=all.lower_bound(size[x]); all.erase(it); chain.insert(size[x]); int smx=0,smn=0; int mx=0,mn=1<<30; int cnt=0; for(int i=head[x];i;i=next[i]) { if(to[i]!=son[x]) { dsu_on_tree(to[i],0); } } if(son[x]) { dsu_on_tree(son[x],1); } for(int i=head[x];i;i=next[i]) { if(to[i]!=son[x]) { cnt++; if(size[to[i]]>mx) { smx=to[i]; mx=size[to[i]]; } if(size[to[i]]<mn) { mn=size[to[i]]; smn=to[i]; } calc(to[i],1); } } if(!son[x]) { ans[x]=n-1; } else { if(x==1) { if(!cnt) { ans[x]=size[son[x]]; } else if(cnt==1) { int w=(size[son[x]]-mx+1)/2; it=sub.lower_bound(w); int res=1<<30; if(it!=sub.end()) { res=min(res,max(size[son[x]]-(*it),mx+(*it))); } if(it!=sub.begin()) { it--; res=min(res,max(size[son[x]]-(*it),mx+(*it))); } ans[x]=res; } else { int w=(size[son[x]]-mn+1)/2; it=sub.lower_bound(w); int res=1<<30; if(it!=sub.end()) { res=min(res,max(size[son[x]]-(*it),max(mn+(*it),mx))); } if(it!=sub.begin()) { it--; res=min(res,max(size[son[x]]-(*it),max(mn+(*it),mx))); } ans[x]=res; } } else { cnt++; if(n-size[x]>mx) { mx=n-size[x]; smx=1; } if(n-size[x]<mn) { mn=n-size[x]; smn=1; } if(mx<size[son[x]]) { if(cnt==1) { int w=(size[son[x]]-mx+1)/2; it=sub.lower_bound(w); int res=1<<30; if(it!=sub.end()) { res=min(res,max(size[son[x]]-(*it),mx+(*it))); } if(it!=sub.begin()) { it--; res=min(res,max(size[son[x]]-(*it),mx+(*it))); } ans[x]=res; } else { int w=(size[son[x]]-mn+1)/2; it=sub.lower_bound(w); int res=1<<30; if(it!=sub.end()) { res=min(res,max(size[son[x]]-(*it),max(mn+(*it),mx))); } if(it!=sub.begin()) { it--; res=min(res,max(size[son[x]]-(*it),max(mn+(*it),mx))); } ans[x]=res; } } else { if(cnt==1) { int w=(mx-size[son[x]]+1)/2; it=chain.lower_bound(w+size[x]); int res=1<<30; if(it!=chain.end()) { res=min(res,max(mx-(*it)+size[x],size[son[x]]+(*it)-size[x])); } if(it!=chain.begin()) { it--; res=min(res,max(mx-(*it)+size[x],size[son[x]]+(*it)-size[x])); } it=all.lower_bound(w); if(it!=all.end()) { res=min(res,max(mx-(*it),size[son[x]]+(*it))); } if(it!=all.begin()) { it--; res=min(res,max(mx-(*it),size[son[x]]+(*it))); } ans[x]=res; } else { int w=(mx-mn+1)/2; it=chain.lower_bound(w+size[x]); int res=1<<30; if(it!=chain.end()) { res=min(res,max(max(mx-(*it)+size[x],mn+(*it)-size[x]),size[son[x]])); } if(it!=chain.begin()) { it--; res=min(res,max(max(mx-(*it)+size[x],mn+(*it)-size[x]),size[son[x]])); } it=all.lower_bound(w); if(it!=all.end()) { res=min(res,max(max(mx-(*it),mn+(*it)),size[son[x]])); } if(it!=chain.begin()) { it--; res=min(res,max(max(mx-(*it),mn+(*it)),size[son[x]])); } ans[x]=res; } } } } if(!opt) { calc(x,0); if(son[x]) { calc(son[x],3); } } else { sub.insert(size[x]); for(int i=head[x];i;i=next[i]) { if(to[i]!=son[x]) { calc(to[i],2); } } } it=chain.lower_bound(size[x]); chain.erase(it); } int main() { scanf("%d",&n); for(int i=2;i<=n;i++) { scanf("%d",&x); add(x,i); } dfs(1); dsu_on_tree(1,1); for(int i=1;i<=n;i++) { printf("%d ",ans[i]); } }