1. 程式人生 > >【雜題】[51Nod 1367] 完美森林【貪心】

【雜題】[51Nod 1367] 完美森林【貪心】

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);
}