【杂题】[51Nod 1367] 完美森林【贪心】

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hzj1054689699/article/details/83344151

Description

给定一棵标号从0开始的n个节点的树,边有长度。
你可以删掉一些边使得这棵树分裂成若干棵树,形成一个森林。
问最少分裂成多少棵树,使得每棵树的直径都不超过L

n 500000 , L 2000000 n\leq 500000,L\leq 2000000

Solution

这题我已开始还想复杂了,往DP方面想

其实并不需要。
随便给这棵树定一个根,从叶子向上合并。

d s t [ i ] dst[i] 表示以 i i 为根的子树中距离它最远的叶子到它的距离。

对于每一个节点i,将它的所有儿子p按照 d s t [ p ] + l e n g t h ( i , p ) dst[p]+length(i,p) 排序
若最大值+次大值大于限制了,那么删掉连向这棵子树的边,次大值变为最大值,一直到合法为止。

此时重新维护这个节点的dst,就是删剩下的最大值。
这样就做完了…
正确性似乎很显然…

复杂度 O ( n log n ) O(n\log n)

Code

#include <cstdio>
#include <iostream>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define N 500005
#define LL long long
using namespace std;
int n,m,m1,ans,fs[N],nt[2*N],dt[2*N],pr[2*N],dst[N],d[N];
void read(int &x)
{
	char ch=getchar();x=0;
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
}
void link(int x,int y,int z)
{
	nt[++m1]=fs[x];
	dt[fs[x]=m1]=y;
	pr[m1]=z;
}
void dfs(int k,int fa)
{
	for(int i=fs[k];i;i=nt[i]) if(dt[i]!=fa) dfs(dt[i],k);	
	d[0]=0;
	for(int i=fs[k];i;i=nt[i]) if(dt[i]!=fa) d[++d[0]]=pr[i]+dst[dt[i]];
	sort(d+1,d+d[0]+1);
	while(d[0]>1&&d[d[0]]+d[d[0]-1]>m) ans++,d[d[0]--]=0; 
	if(d[d[0]]>m) ans++,d[d[0]--]=0;
	dst[k]=d[d[0]];
}
int main()
{
	cin>>n>>m;
	fo(i,1,n-1) 
	{
		int x,y,z;
		read(x),read(y),read(z);
		x++,y++;
		link(x,y,z),link(y,x,z);
	}
	dfs(1,0);
	printf("%d\n",ans+1);
}

猜你喜欢

转载自blog.csdn.net/hzj1054689699/article/details/83344151
今日推荐