题面
题意:对于每个点单独考虑,删掉它,你可以把一个节点的父亲改成另一个节点(只能做一次),使森林中最大的树size最小。
这题的思路比较简单,对于森林中的所有树,显然应该把最大的那颗树的一部分接到最小的上,然后对次大树,最大树·改,最小树·改取max
然后如何找最优的呢?
显然减(mx-cx)/2是最优的,然后就开权值线段树,查询1~(mx-cx)/2最大值
查询(mx-cx)/2~n最小值
对于最大树是儿子,就按照树上dsu合并;
如果是父亲,分成两部分,
一部分是到根路径上的点,查询时l,r加上siz[u],因为他们的size要减siz[u]
另一部分是全局的减去当前子树的,不包含到根路径上的点
#include
#include
#include
using namespace std;
const int N=2e5+7,inf=1e8;
struct edge {
int t,nxt;
}e[N];
struct node {
int s,tag;
};
int n,cnt,rt;
int head[N],siz[N],Mx[N],Cx[N],Mn[N],Mson[N],Bigs[N],ans[N],vis[N];
void add(int u,int t) {
e[++cnt].t=t;e[cnt].nxt=head[u];head[u]=cnt;
}
struct tree {
#define ls ts<<1
#define rs ts<<1|1
node tr[N<<1];
void pushdown(int ts) {
if (!tr[ts].tag) return;
tr[ls].s=0;tr[rs].s=0;
tr[ls].tag=1;tr[rs].tag=1;
tr[ts].tag=0;
}
inline void add(int ts,int l,int r,int pos,int x) {
pushdown(ts);
tr[ts].s+=x;
if (l==r) return;
int mid=(l+r)>>1;
if (pos<=mid) add(ls,l,mid,pos,x);
else add(rs,mid+1,r,pos,x);
}
inline void clear() {
tr[1].s=0;tr[1].tag=1;
}
inline int query2(int ts,int l,int r,int L,int R) {
if (L>R || !tr[ts].s) return 0;
if (l==r) return l;
pushdown(ts);
int mid=(l+r)>>1;
if (R<=mid) return query2(ls,l,mid,L,R);
else if (L>mid) return query2(rs,mid+1,r,L,R);
else {
int v=query2(rs,mid+1,r,mid+1,R);
if (v) return v;
else return query2(ls,l,mid,L,mid);//1~(mx-cx)/2最大值
}
}
inline int query3(int ts,int l,int r,int L,int R) {
if (L>R || !tr[ts].s) return inf;
if (l==r) return l;
pushdown(ts);
int mid=(l+r)>>1;
if (R<=mid) return query3(ls,l,mid,L,R);
else if (L>mid) return query3(rs,mid+1,r,L,R);
else {
int v=query3(ls,l,mid,L,mid);
if (v!=inf) return v;
else return query3(rs,mid+1,r,mid+1,R);//(mx-cx)/2~n最小值
}
}
}T1,T2,T3;//T1总共的siz,T2所有祖先的siz,T3节点当前siz
void chu(int &mx,int &cx,int &mn,int v,int &id,int t) {
if (v>mx) cx=mx,mx=v,id=t;
else if (v>cx) cx=v;
if (v) mn=min(mn,v);
}//找最大次大最小
void dfs1(int u) {
siz[u]=1;
Mson[u]=-1;
for (int i=head[u];i;i=e[i].nxt) {
int t=e[i].t;
dfs1(t);
siz[u]+=siz[t];
chu(Mx[u],Cx[u],Mn[u],siz[t],Mson[u],t);
}
Bigs[u]=Mson[u];
chu(Mx[u],Cx[u],Mn[u],n-siz[u],Mson[u],0);
T1.add(1,1,n,siz[u],1);
}
void Add(int u) {
T3.add(1,1,n,siz[u],1);
for (int i=head[u];i;i=e[i].nxt) {
int t=e[i].t;
if (vis[t]) continue;
Add(t);
}
}
int query2(int ts,int l,int r,int L,int R) {
if (L>R || T1.tr[ts].s-T3.tr[ts].s<=0) return 0;
if (l==r) return l;
T3.pushdown(ts);
int mid=(l+r)>>1;
if (R<=mid) return query2(ls,l,mid,L,R);
else if (L>mid) return query2(rs,mid+1,r,L,R);
else {
int v=query2(rs,mid+1,r,mid+1,R);
if (v) return v;
else return query2(ls,l,mid,L,mid);
}//父亲查找max
}
int query3(int ts,int l,int r,int L,int R) {
if (L>R || T1.tr[ts].s-T3.tr[ts].s<=0) return inf;
if (l==r) return l;
T3.pushdown(ts);
int mid=(l+r)>>1;
if (R<=mid) return query3(ls,l,mid,L,R);
else if (L>mid) return query3(rs,mid+1,r,L,R);
else {
int v=query3(ls,l,mid,L,mid);
if (v!=inf) return v;
else return query3(rs,mid+1,r,mid+1,R);
}//父亲查找min
}
int chuu(int a,int b) {
return (a+b)&1;
}
void dfs2(int u) {
T1.add(1,1,n,siz[u],-1);
T2.add(1,1,n,siz[u],1);
for (int i=head[u];i;i=e[i].nxt) {
int t=e[i].t;
if (t==Bigs[u]) continue;
dfs2(t);
T3.clear();
}
if (Bigs[u]!=-1) dfs2(Bigs[u]),vis[Bigs[u]]=1;
int v=0;
if (Mson[u]==Bigs[u]) {
v=T3.query2(1,1,n,1,(Mx[u]-Mn[u])/2+chuu(Mx[u],Mn[u]));
ans[u]=max(max(Mx[u]-v,Mn[u]+v),Cx[u]);
v=T3.query3(1,1,n,(Mx[u]-Mn[u])/2,n);
ans[u]=min(ans[u],max(max(Mx[u]-v,Mn[u]+v),Cx[u]));
}
Add(u);
if(Bigs[u]!= -1) vis[Bigs[u]] = 0;
T1.add(1,1,n,siz[u],1);
T2.add(1,1,n,siz[u],-1);
if (Mson[u]!=Bigs[u]) {
v=max(query2(1,1,n,1,(Mx[u]-Mn[u])/2+chuu(Mx[u],Mn[u])),T2.query2(1,1,n,1+siz[u],min((Mx[u]-Mn[u])/2+siz[u]+chuu(Mx[u],Mn[u]),n))-siz[u]);
ans[u]=max(max(Mx[u]-v,Mn[u]+v),Cx[u]);
v=min(query3(1,1,n,(Mx[u]-Mn[u])/2,n),T2.query3(1,1,n,(Mx[u]-Mn[u])/2+siz[u],n)-siz[u]);
ans[u]=min(ans[u],max(max(Mx[u]-v,Mn[u]+v),Cx[u]));
}
}
int main() {
scanf("%d",&n);
if (n==1) return puts("0"),0;
memset(Mn,60,sizeof Mn);
for (int i=1,u,v;i<=n;i++) {
scanf("%d%d",&u,&v);
if (u==0) rt=v;
else add(u,v);
}
dfs1(rt);
dfs2(rt);
for (int i=1;i<=n;i++) printf("%d\n",ans[i]);
}