码迷,mamicode.com
首页 > 其他好文 > 详细

【YbtOJ#662】交通运输

时间:2021-02-24 13:09:15      阅读:0      评论:0      收藏:0      [点我收藏+]

标签: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;
}

【YbtOJ#662】交通运输

标签:tree   size   节点   problem   大连   最大的   思路   tin   inline   

原文地址:https://www.cnblogs.com/stoorz/p/14437674.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!