这题一眼看上去十分的吓人
于是我决定写一个暴力然后过了…奇妙
首先看到所求的最大值最小,我们能确定肯定用二分
但是如何保证森林里的最大直径小于所二分的值呢
我们按照天天爱跑步的存边长度方式,树上链的形成
分为两种
1、该点子节点的子链的最大值和次大值
2、该点最长链加上面的部分
那么我们如何保证链最小呢,我们先不管第二点,我们把该点的子链长度放入一个vector
排序后,每次取出最大值和最小值,排除最大值,直到最大加次打小于所二分的值
此时该点的最长链的长度就是最大值
那么第二点如何处理呢,实际上不用管,因为在根节点的分叉点只存在情况一,我们只需要
每次求出合法的最大子链长度即可,于是就是一个很简单的处理了
PS.我的写法有一丢丢问题,所以加了一点点特判
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
using namespace std;
const int maxn = 100007;
const int INF = 2147483647;
struct node{
int to,next,w;
}edge[maxn*2];
int cnt,head[maxn];
void add(int from,int to){
edge[++cnt].to=to;
edge[cnt].next=head[from];
head[from]=cnt;
}
int cut,leaf[maxn],n,m;
bool cmp1(int a,int b){return a>b;}
void dp(int u,int fa,int ML){
//cout<<u<<endl;
int son=0;
vector<int>me;
for(int i=head[u];i;i=edge[i].next){
int to=edge[i].to;
if(to==fa)continue;
son++;
dp(to,u,ML);
me.push_back(leaf[to]+1);
}
if(son==0)return;
sort(me.begin(),me.end(),cmp1);
/*cout<<u<<" :";
for(int i=0;i<me.size();i++)cout<<me[i]<<" ";
cout<<endl;*/
if(me[son-1]>ML){
cut+=son;
return;
}
int temp=0;
leaf[u]=me[temp++];
while(temp<me.size()&&leaf[u]+me[temp]>ML){
cut++;leaf[u]=me[temp];temp++;
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++){
int x,y;
scanf("%d%d",&x,&y);
add(x,y),add(y,x);
}
int l=1,r=n-1,ans=INF;
while(l<=r){
int mid=l+r>>1;
//cout<<l<< " "<<r<< " "<<mid<<endl;
memset(leaf,0,sizeof(leaf));
cut=0,dp(1,0,mid);
/*cout<<endl<<cut<<endl;
for(int i=1;i<=n;i++)cout<<leaf[i]<<" ";
cout<<endl;*/
if(cut>m)l=mid+1;
else ans=min(ans,mid),r=mid-1;
}
cout<<ans;
}