• 【YbtOJ#662】交通运输


    题目

    题目链接:https://www.ybtoj.com.cn/problem/662

    (n leq 10^5)

    思路

    考虑一个点 (x),肯定是选择删去它后最大的连通块的一个子树连接到最小的连通块的一个子树。答案范围在次大连通块大小和最大连通块大小之间。
    显然这个东西是有单调性的,考虑二分答案 (mid),设最大最小的连通块大小为 (s1,s2),那么我们只需要知道最大的连通块内是否有一个子树大小在 ([s2-mid,mid-s1]) 之内就可以了。
    如果最大连通块是 (x) 的子节点为根的子树,这个玩意直接线段树合并就可以了。
    如果最大连通块是 (x) 的父亲所在连通块,我们发现 (x) 的祖先节点的大小会减小 ( ext{siz}_x),其他节点不变。所以我们可以用所有大小在 ([s2-mid,mid-s1]) 的点的数量,减去 (x) 子树内在范围内点的数量,再减去 (x) 祖先节点在范围内的数量,加上 (x) 祖先在 ([s2-mid+ ext{siz}_x,mid-s1+ ext{siz}_x]) 的数量即可。
    用一个树状数组再维护一下 (x) 祖先就好了。
    时间复杂度 (O(nlog^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
    ",ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    三、sersync+rsync实现服务器文件实时同步
    二、Linux实时同步软件之inotify
    一、rsync基础原理
    Samba实战
    DHCP企业实战
    NTP服务器企业实战
    Vsftpd服务器原理及部署
    Python的五大数据类型的作用、定义方式、使用方法,两种交互式方式,格式化输出的三种方式练习。
    pycharm快捷键,变量,字符串,类型的操作方法
    python基础归纳练习 python两种方式,垃圾回收机制,小数整池,数字类型,字符串类型。
  • 原文地址:https://www.cnblogs.com/stoorz/p/14437674.html
Copyright © 2020-2023  润新知