【JZOJ5963】【NOIP2018】赛道修建

description

C城将要举办一系列的赛车比赛。在比赛前,需要在城内修建m条赛道。
C城一共有n个路口,这些路口编号为1,2,…,n ,有n-1 条适合于修建赛道的双向通行的道路,每条道路连接着两个路口。其中,第i条道路连接的两个路口编号为ai 和bi ,该道路的长度为li 。借助这n-1 条道路,从任何一个路口出发都能到达其他所有的路口。
一条赛道是一组互不相同的道路e1,e2,…,ek ,满足可以从某个路口出发,依次经过道路 e1,e2,…,ek(每条道路经过一次,不允许调头)到达另一个路口。一条赛道的长度等于经过的各道路的长度之和。为保证安全,要求每条道路至多被一条赛道经过。
目前赛道修建的方案尚未确定。你的任务是设计一种赛道修建的方案,使得修建的m条赛道中长度最小的赛道长度最大(即m条赛道中最短赛道的长度尽可能大)。


analysis

  • 正解贪心+二分,想正解前先考虑前 55 p t s 55pts 55pts的分数

  • 对于一条链,二分最小链长度 m i d mid mid,从链头开始贪心地加边,并判断链个数是否大于 m m m

  • 对于菊花图,如要使最短链最长,那么就把所有的边排降序,易知大边连小边一定最优
    那么就贪心地让最大边连最小边、次大边连次小边等等,其中边权和最小的即为答案

  • 对于分支不超过三的情况,说明这棵树是个二叉树,我们需要从二叉树上面抠出 m m m条链
    考虑一下,既然是抠链,要么是这两个儿子节点(及其儿孙节点)自己连成一条链
    要么是拿左右儿子(及其儿孙节点)其中一个与当前节点连成链,那么连较长的儿子的链贡献会优

  • 由二叉树的情况推广到一般情况,首先还是二分最小链长度 m i d mid mid,贪心去匹配每个儿子的链
    对于当前节点的所有儿子和该儿子节点中最大的儿子贡献的权值和拉出来排一个序
    删去其中可以单独成为一条链(即这种方案的权值已经 > m >m >m了)的情况
    对于当前儿子中最小的链,再用一个二分找出一种和它匹配后的权值恰好 > m >m >m的情况,用 v e c t o r vector vector实现
    如果当前儿子中最小链与最长链都无法匹配,就尝试次小值与最长链匹配,以此类推
    对于无法匹配的短边以及匹配剩余的最后一条边,我们记录下来最大值,因为可能与父亲节点匹配

  • 那么这题就搞完了

  • 注意对于菊花图,我们还是用回菊花图的做法

  • 时间复杂度 O ( n log ⁡ 2 2 n ) O(n\log_2^2 n) O(nlog22n)


code

#pragma GCC optimize("O3")
#pragma G++ optimize("O3")
#include
#include
#include
#include
#define MAXN 50005
#define MAXM MAXN*2
#define ll long long
#define reg register ll
#define fo(i,a,b) for (ll i=a;i<=b;++i)
#define fd(i,a,b) for (ll i=a;i>=b;--i)
#define rep(i,a) for (reg i=last[a];i;i=next[i])
#define O3 __attribute__((optimize("-O3")))

using namespace std;

ll last[MAXM],next[MAXM],tov[MAXM],len[MAXM];
ll a[MAXN],f[MAXN];
ll n,m,ans,tot,sum,total;
bool flag=1;

O3 inline ll read()
{
	ll x=0,f=1;char ch=getchar();
	while (ch<'0' || '9'<ch){if (ch=='-')f=-1;ch=getchar();}
	while ('0'<=ch && ch<='9')x=x*10+ch-'0',ch=getchar();
	return x*f;
}
O3 inline void link(ll x,ll y,ll z)
{
	next[++tot]=last[x],last[x]=tot,tov[tot]=y,len[tot]=z;
}
O3 inline bool cmp(ll x,ll y)
{
	return x>y;
}
O3 inline void dfs(ll x,ll y,ll z)
{
	vector<ll> v;
	rep(i,x)if (tov[i]!=y)
	{
		dfs(tov[i],x,z);
		v.push_back(f[tov[i]]+len[i]);
	}
	sort(v.begin(),v.end());
	while (v.size() && v[v.size()-1]>=z)
	{
		++total,v.pop_back();
	}
	while (v.size()>=2)
	{
		if (v[0]+v[v.size()-1]>=z)
		{
			ll l=0,r=v.size(),ans=-1;
			while (l<=r)
			{
				ll mid=(l+r)>>1;
				(v[0]+v[mid]>=z && mid!=0)?ans=mid,r=mid-1:l=mid+1;
			}
			++total;
			v.erase(v.begin()+ans),v.erase(v.begin());
		}
		if (v.size() && v[0]+v[v.size()-1]<z)
		{
			f[x]=max(f[x],v[0]),v.erase(v.begin());
		} 
	}
	if (v.size())
	{
		f[x]=max(f[x],v[v.size()-1]),v.pop_back();
	}
}
O3 inline bool jugde(ll mid)
{
	memset(f,0,sizeof(f));
	total=0,dfs(1,0,mid);
	return total>=m;
}
O3 int main()
{
	freopen("track.in","r",stdin);
	freopen("track.out","w",stdout);
	n=read(),m=read();
	fo(i,1,n-1)
	{
		ll x=read(),y=read(),z=read();
		link(x,y,z),link(y,x,z),a[i]=z,sum+=z;
		if (x!=1)flag=0;
	}
	if (flag)
	{
		ans=1e10;
		sort(a+1,a+n,cmp);
		fo(i,1,m)ans=min(ans,a[i]+a[2*m-i+1]);
		printf("%lld\n",ans);
		return 0;
	}
	ll l=0,r=sum/m+1;
	while (l<=r)
	{
		ll mid=(l+r)>>1;
		jugde(mid)?ans=mid,l=mid+1:r=mid-1;
	}
	printf("%lld\n",ans);
	return 0;
}

你可能感兴趣的:(分治,贪心,NOIP)