题意:给定一棵树,将叶子节点划分成若干个集合,集合内的叶子节点两两距离小于$K$,问最小划分的集合数。
想歪了,感觉这类题目自己做得好虚啊,不知道为什么能想到这样的思路。
正解就是对于每一个儿子,保存最大距离的叶子,然后排序之后,找到一$R$使得$[1,R]$可以在一个集合内。
然后将$[R+1,son]$内的点都各自成立一个集合。然后这个节点作为儿子的返回值为R点的$dis+1$。
证明:显然的一点是将$[1,R]$内的点并起来是优的。我们只需要证明为什么直接把$[R+1,son]$单独构建最优即可。
若$R+1$和子树外的一个点$a$在一个集合内,$R$和子树外一个点$b$在一个集合内,我们只需证明$a b R$能在一个集合内即可。
只需要根据$dis[R][R+1]>dis[a][R+1]$来证明即可。
$O(n*logn)$
#include <bits/stdc++.h> #define min(a,b) ((a)<(b)?(a):(b)) #define max(a,b) ((a)>(b)?(a):(b)) #define for1(a,b,i) for(int i=a;i<=b;++i) #define FOR2(a,b,i) for(int i=a;i>=b;--i) using namespace std; typedef long long ll; inline int read() { int f=1,sum=0; char x=getchar(); for(;(x<'0'||x>'9');x=getchar()) if(x=='-') f=-1; for(;x>='0'&&x<='9';x=getchar()) sum=sum*10+x-'0'; return f*sum; } #define M 1000005 int n,m; int ans,e_size,head[M],du[M]; struct node {int v,nxt;}e[M*2]; inline void e_add(int u,int v) { e[++e_size]=(node){v,head[u]}; head[u]=e_size; } inline int dfs(int x,int fa) { vector <int> sta; for(int i=head[x];i;i=e[i].nxt) { int v=e[i].v; if(v==fa) continue; sta.push_back(dfs(v,x)+1); } if(!sta.size()) return 0; sort(sta.begin(),sta.end()); int size=sta.size(); while (size>=2) { //cout<<size<<" "<<sta.size()<<endl; if(sta[size-2]+sta[size-1]<=m) break; --size,++ans,sta.pop_back(); } return sta[size-1]; } int main () { //freopen("a.in","r",stdin); n=read(),m=read(); for1(2,n,i) { int x=read(),y=read(); ++du[x],++du[y]; e_add(x,y),e_add(y,x); } int root=0; for1(1,n,i) if(du[i]>1) root=i; dfs(root,0); cout<<ans+1<<endl; }