算法:玄学!贪心、树形结构
难度:NOIP+
题解:
我们dfs从下到上合并每一棵子树,对于一个点x所有的儿子y,我们把所有dis push_back到一个vector里面排序,然后从大到小扫描每一条边,如果V[i]+V[i-1]+2>K的话,就需要再开一个集合,因为我们是从大到小枚举的,所以如果出现了V[i]+V[i-1]+2<=K的情况时,就可以break了!
最后还有一个集合没有被计算到答案中去,最后ans需要+1。
还有一个细节就是要选择一个非叶子结点做根搜索。(易证!)
时间复杂度:O(nlogn)
代码如下:
#include <bits/stdc++.h>
#define ll long long
#define N 1000005
using namespace std;
struct node
{
int next;
int to;
}edg[N<<1];
int hea[N],cnt=1;
void init()
{
memset(hea,-1,sizeof(hea));
cnt=1;
}
void add(int u,int v)
{
edg[cnt].next=hea[u];
edg[cnt].to=v;
hea[u]=cnt++;
}
int tt=1;
int n,k;
int dfs(int rt,int fa)
{
vector<int>V;//每一层重新定义,否则还得erase!
for(int i = hea[rt];i != -1;i=edg[i].next)
{
int to=edg[i].to;
if(to==fa) continue;
V.push_back(dfs(to,rt)+1);
}
if(V.size()==0)
{
return 0;
}
sort(V.begin(),V.end());
int ii;
for(ii = V.size()-1;ii > 0;ii--)
{
if(V[ii]+V[ii-1]<=k)
{
break;
}
tt++;
}
return V[ii];
}
int deg[N];
int main()
{
scanf("%d%d",&n,&k);
init();
for(int i = 1;i < n;i++)
{
int a,b;
scanf("%d%d",&a,&b);
add(a,b),add(b,a);
deg[a]++,deg[b]++;
}
int sta;
for(int i = 1;i <= n;i++)
{
if(deg[i]>1)
{
sta=i;
break;
}
}
dfs(sta,-1);
printf("%d\n",tt);
return 0 ;
}
如果还是有疑惑,戳->大佬博客!