6538. 【2020.04.04模拟】Delegation 2

Description

Farmer John 的农场由 N 块草地(1≤N≤10^5)和 N−1 条道路组成,满足每块草地都能从任意其他草地到达。也就是说,这个农场组成一棵树。但在与不可避免地由树而生的麻烦的算法问题打了 28 年交道之后,FJ 终于认为树形的农场太复杂了。他相信在链上的算法问题更简单。
所以,他的计划是将这些道路划分成若干条链,并将每条链交给他值得信任的农场工人之一负责。他不关心链的数量。然而,他希望确保这些链都尽可能长,从而不会有农场工人能够用一个渐近效率低下的算法蒙混过关!
 
帮助 Farmer John 求出最大正整数 K,使得道路可以被划分为一些长度至少为 K 的链。
 
测试点性质:
在测试点 2-4 中树组成了一个 “星形”;至多一个结点的度大于二。
测试点 5-8 满足 N≤10^3。
测试点 9-15 没有额外限制。

Input

第一行包含一个整数 N。
以下 N−1 行每行包含两个空格分隔的整数 a 和 b,表示结点 a 与 b 之间有一条边。a 和 b 均在范围 1…N 内。

Output

输出 K。

Sample Input

8
1 2
1 3
1 4
4 5
1 6
6 7
7 8

Sample Output

3
一种可能的划分方式如下:
 
2−1−6−7−8,3−1−4−5

Solutoin

贪心+二分。

首先二分答案。

设f[i]表示以i为根的子树内,链的长度都大于等于当前mid(即子树内满足题意),从i开始往下延伸的最长的链的长度。

但是并不保证i一定要有值,有时没有会为0,有时有值。

考虑f如何转移。

将孩子的f找出来(记得加上孩子到当前根x的一条边),分成两类。

1.长度小于len的链

2.长度大于等于len的链

对于第1类,我们首先要将他们两两组合,使得小于len的链尽量少,这里就是通过当前根将两个孩子往下延伸的链连起来。

那么我们可以维护一个从前往后和一个从后往前的指针,碰到两个相加大于len的就标记下来。

对于剩下的小于len的链,只能借助长度大于等于len的链进行合并,并且任意合并长度一定大于len。

记当前剩下的长度小于len的链的个数为A,大于等于的个数为B。

1.当A>B+1时,剩下两个或以上的不能匹配完,不满足题意,直接判mid不成立。

2.当A=B+1时,f[x]就取A的最大的一个没匹配完的长度(因为一个点的f值越大越好)。

3.当A<B+1时(A、B为非负整数,则又可写作A<=B时),

     3.1当A<B时,f[x]取B中最长的一个的长度(即最后一个)。

     3.2当A=B时,判断当前根是否为1,如果为1则直接组合。否则f[x]取A中最大的未匹配的一个长度。

最后注意特判在最优的情况下1号根节点是否能刚好匹配完(即f[1]=0),则满足要求。

Code

#include<cstdio>
#include<algorithm>
#include<cstring>
#define I int
#define F(i,a,b) for(register int i=a;i<=b;++i)
#define Fd(i,a,b) for(register int i=a;i>=b;--i)
#define N 100002
using namespace std;
I n,f[N],t[N<<1],nx[N<<1],ls[N],a[N],b[N],A,B,x,y,l,r,mid,ans,T,w;
I R(I &x){
	x=0;char ch=getchar();for(;ch<'0'||ch>'9';ch=getchar());
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';	
}
void add(I x,I y){t[++T]=y,nx[T]=ls[x],ls[x]=T;}
I dg(I x,I y,I L){
	for(I k=ls[x];k;k=nx[k]) if(t[k]!=y&&!dg(t[k],x,L)) return 0;
	f[x]=A=B=T=0;I z;
	for(I k=ls[x];k;k=nx[k]) if((z=t[k])!=y) (f[z]+1<L)?(a[++A]=f[z]+1):(b[++B]=f[z]+1);
	sort(a+1,a+1+A),sort(b+1,b+1+B);w=A;
	F(i,1,A) if(i<w&&a[i]+a[w]>=L){a[i]=a[w--]=0;T+=2;}A-=T;
	if(A<B) f[x]=b[B];
	else if(A-B==1||(x!=1&&A==B)){Fd(i,A+T,1) if(a[i]){f[x]=a[i];break;}}
	return (A-B-1>0)||(x==1&&f[x]&&f[x]<L)?0:1;
}
I main(){
	freopen("deleg.in","r",stdin);
	freopen("deleg.out","w",stdout);
	R(n);
	F(i,1,n-1){R(x),R(y),add(x,y),add(y,x);}
	for(l=1,r=n,mid=l+r>>1;l<=r;mid=l+r>>1){if(dg(1,0,mid)) l=(ans=mid)+1;else r=mid-1;}
	printf("%d\n",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/zsjzliziyang/article/details/105375432
今日推荐