题目大意:
给定一棵树,对于树上每个结点,将它删去,然后你可以将得到的森林中任意一棵树的一个点与其父亲断开并连接到另一颗树上。求森林中所有树 size 最大值的最小值。
先考虑怎么求一个点的答案。令
sizei
表示点
i
的子树和,
注意二分的边界。
从根开始
dfs
一遍,维护
4
个
-
mappath
:从根到点
i
父亲所有点的
size -
mapheaviest
:点
i
重儿子的子树中所有点的
size -
maplight
:点
i
所有轻儿子的子树中所有点的
size - mapother :除上述 map 中所有点之外其它点的 size
求点 i 的答案时,判断删去这个点后森林中最大的树是什么:
- 如果是点
i 的重儿子,直接二分,在 mapheaviest 上查询即可求出答案。- 如果是根,那么由于在从根到点
i
父亲路径上所有点
size 在删去 i 后要减去sizei ,要特判一下。其他可以在 mapother 上查询。 接下来考虑怎么维护这 4 个
map :- mappath : dfs 时更新,退出时删除即可。
- mapheaviset , maplight :可以用树上启发式合并求。
- mapother :初始时所有点都在内,其他 map 中加入一个点时就删除这个点。
时间复杂度 O(nlogn)
代码:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<map> #include<vector> using namespace std; #define N 100010 #define It map<int,int>::iterator vector<int>g[N]; map<int,int>M[4]; int i,j,k,n,m,p,sz[N],Ans[N],s[N],x,y,mn[N],smx[N],Rt; inline int Min(int x,int y){ return x<y?x:y; } inline int Max(int x,int y){ return x<y?y:x; } inline void Dfs1(int x){ sz[x]=1;mn[x]=n; for(int i=0;i<g[x].size();i++){ Dfs1(g[x][i]); sz[x]+=sz[g[x][i]]; mn[x]=Min(mn[x],sz[g[x][i]]); if(sz[g[x][i]]>sz[s[x]])smx[x]=sz[s[x]],s[x]=g[x][i];else if(sz[g[x][i]]>smx[x])smx[x]=sz[g[x][i]]; } M[3][sz[x]]++; if(sz[x]<n)mn[x]=Min(mn[x],n-sz[x]); } inline void Update(int x,int y,int d){ M[d][x]+=y;M[3][x]-=y; if(!M[3][x])M[3].erase(x); if(!M[d][x])M[d].erase(x); } inline void Get(int x,int y,int d){ Update(sz[x],y,d); for(int i=0;i<g[x].size();i++)Get(g[x][i],y,d); } inline int Find(int x,int y,int l,int r,int d,int z){ int Mid; if(x==y&&x>=l&&x<=r)return x; while(l<=r){ Mid=l+r>>1; It t=M[d].lower_bound(y-Mid+z); if(t!=M[d].end()&&t->first<=Mid-x+z)r=Mid-1;else l=Mid+1; } return l; } inline void Solve(int x){ if(sz[x]==n)Ans[x]=Find(mn[x],sz[s[x]],smx[x],sz[s[x]],1,0);else if(sz[s[x]]>n-sz[x])Ans[x]=Find(Min(mn[x],n-sz[x]),sz[s[x]],Max(n-sz[x],smx[x]),sz[s[x]],1,0);else if(!s[x])Ans[x]=n-sz[x];else Ans[x]=Min(Find(mn[x],n-sz[x],sz[s[x]],n-sz[x],0,sz[x]),Find(mn[x],n-sz[x],sz[s[x]],n-sz[x],3,0)); } inline void Dfs(int x,bool d){ Update(sz[x],1,0); for(int i=0;i<g[x].size();i++) if(g[x][i]!=s[x])Dfs(g[x][i],0); if(s[x])Dfs(s[x],1); for(int i=0;i<g[x].size();i++) if(g[x][i]!=s[x])Get(g[x][i],1,2); if(!(--M[0][sz[x]]))M[0].erase(sz[x]); Solve(x); M[3][sz[x]]++; if(d){ Update(sz[x],1,1); for(It t=M[2].begin();t!=M[2].end();) M[1][t->first]+=t->second,M[2].erase(t++); }else for(int i=0;i<g[x].size();i++)if(g[x][i]!=s[x])Get(g[x][i],-1,2);else Get(g[x][i],-1,1); } int main(){ scanf("%d",&n); if(n==1)return putchar(48),0; for(i=1;i<=n;i++){ scanf("%d%d",&x,&y); if(x)g[x].push_back(y);else Rt=y; } Dfs1(Rt); Dfs(Rt,1); for(i=1;i<=n;i++)printf("%d\n",Ans[i]); return 0; }
- 如果是根,那么由于在从根到点
i
父亲路径上所有点