标签:tree size 节点 problem 大连 最大的 思路 tin inline
题目链接:https://www.ybtoj.com.cn/problem/662
\(n \leq 10^5\)。
考虑一个点 \(x\),肯定是选择删去它后最大的连通块的一个子树连接到最小的连通块的一个子树。答案范围在次大连通块大小和最大连通块大小之间。
显然这个东西是有单调性的,考虑二分答案 \(mid\),设最大最小的连通块大小为 \(s1,s2\),那么我们只需要知道最大的连通块内是否有一个子树大小在 \([s2-mid,mid-s1]\) 之内就可以了。
如果最大连通块是 \(x\) 的子节点为根的子树,这个玩意直接线段树合并就可以了。
如果最大连通块是 \(x\) 的父亲所在连通块,我们发现 \(x\) 的祖先节点的大小会减小 \(\text{siz}_x\),其他节点不变。所以我们可以用所有大小在 \([s2-mid,mid-s1]\) 的点的数量,减去 \(x\) 子树内在范围内点的数量,再减去 \(x\) 祖先节点在范围内的数量,加上 \(x\) 祖先在 \([s2-mid+\text{siz}_x,mid-s1+\text{siz}_x]\) 的数量即可。
用一个树状数组再维护一下 \(x\) 祖先就好了。
时间复杂度 \(O(n\log^2 n)\)。
#include <bits/stdc++.h>
using namespace std;
const int N=100010,LG=18,MAXN=N*LG*4;
int n,tot,ans[N],fa[N],head[N],cnt[N],rt[N],siz[N],maxp[N][3],son[N];
struct edge
{
int next,to;
}e[N];
void add(int from,int to)
{
e[++tot]=(edge){head[from],to};
head[from]=tot;
}
struct BIT
{
int c[N];
void add(int x,int v)
{
for (int i=x;i<=n;i+=i&-i)
c[i]+=v;
}
int query(int x)
{
int ans=0;
for (int i=min(x,n);i>0;i-=i&-i)
ans+=c[i];
return ans;
}
}bit;
struct SegTree
{
int tot,lc[MAXN],rc[MAXN],sum[MAXN];
int update(int now,int l,int r,int k)
{
int x=++tot;
lc[x]=lc[now]; rc[x]=rc[now]; sum[x]=sum[now]+1;
if (l==r) return x;
int mid=(l+r)>>1;
if (k<=mid) lc[x]=update(lc[now],l,mid,k);
else rc[x]=update(rc[now],mid+1,r,k);
return x;
}
int query(int x,int l,int r,int ql,int qr)
{
if (!x || ql>qr) return 0;
if (ql<=l && qr>=r) return sum[x];
int mid=(l+r)>>1,ans=0;
if (ql<=mid) ans+=query(lc[x],l,mid,ql,qr);
if (qr>mid) ans+=query(rc[x],mid+1,r,ql,qr);
return ans;
}
int merge(int x,int y)
{
if (!x || !y) return x|y;
int p=++tot;
sum[p]=sum[x]+sum[y];
lc[p]=merge(lc[x],lc[y]);
rc[p]=merge(rc[x],rc[y]);
return p;
}
}seg;
void ins(int x,int s,int v)
{
if (s>maxp[x][0]) maxp[x][1]=maxp[x][0],maxp[x][0]=s,son[x]=v; else
if (s>maxp[x][1]) maxp[x][1]=s;
if (!maxp[x][2] || s<maxp[x][2]) maxp[x][2]=s;
}
void dfs1(int x)
{
siz[x]=1;
for (int i=head[x];~i;i=e[i].next)
{
int v=e[i].to;
dfs1(v); siz[x]+=siz[v];
rt[x]=seg.merge(rt[x],rt[v]);
ins(x,siz[v],v);
}
rt[x]=seg.update(rt[x],1,n,siz[x]);
cnt[siz[x]]++;
if (fa[x]) ins(x,n-siz[x],fa[x]);
}
void dfs2(int x)
{
int l=maxp[x][1],r=maxp[x][0],mid;
if (!l) l=r;
while (l<=r)
{
mid=(l+r)>>1;
int qr=mid-maxp[x][2],ql=maxp[x][0]-mid;
if (!ql) { r=mid-1; continue; }
if (ql>qr) { l=mid+1; continue; }
if (son[x]!=fa[x])
{
if (seg.query(rt[son[x]],1,n,ql,qr)-(ql<=siz[x])*(qr>=siz[x])) r=mid-1;
else l=mid+1;
}
else
{
if (cnt[qr]-cnt[ql-1]-seg.query(rt[x],1,n,ql,qr)-(bit.query(qr)-bit.query(ql-1))+(bit.query(qr+siz[x])-bit.query(ql+siz[x]-1))) r=mid-1;
else l=mid+1;
}
}
ans[x]=r+1;
bit.add(siz[x],1);
for (int i=head[x];~i;i=e[i].next)
dfs2(e[i].to);
bit.add(siz[x],-1);
}
int main()
{
freopen("traffic.in","r",stdin);
freopen("traffic.out","w",stdout);
memset(head,-1,sizeof(head));
scanf("%d",&n);
for (int i=2;i<=n;i++)
scanf("%d",&fa[i]),add(fa[i],i);
dfs1(1);
for (int i=1;i<=n;i++) cnt[i]+=cnt[i-1];
dfs2(1);
for (int i=1;i<=n;i++)
printf("%d\n",ans[i]);
return 0;
}
标签:tree size 节点 problem 大连 最大的 思路 tin inline
原文地址:https://www.cnblogs.com/stoorz/p/14437674.html