目录
显示
题目链接
https://www.luogu.com.cn/problem/P6142
题解
比赛结束后才发现自己做的是赛道修建(打的是金组,不过金组这题其实做法一样)。
首先二分答案 \(K\),如何检验这个 \(K\) 是可以的呢?做法和 同名金组题目 差不多。
对于一个点 \(u\),经过 \(u\) 和其祖先节点的链最多只有一条,我们贪心地留一条最长的子链。
因此处理到点 \(u\) 的时候,还是把点 \(u\) 的所有子链拉出来,将所有链排个序。
二分找要留给祖先点的子链,检验的时候看看剩下的链能否全部配成长度大于等于 \(K\) 的链即可(这里为了减少一些奇怪的特判,子链条数为偶数的时候可以加一条长度为零的链)。
#include <cstring>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
vector<int> vec[100005],e[100005];
int s[100005];
bool check2(int u,int p,int x)
{
int siz=vec[u].size();
for(int i=0,j=siz-1;i<j;i++,j--)
{
if(i==p)i++;
if(j==p)j--;
if(vec[u][i]+vec[u][j]<x)return false;
}
return true;
}
bool check(int u,int fa,int x)
{
vec[u].clear();
for(auto v:e[u])
{
if(v==fa)continue;
if(!check(v,u,x))return false;
vec[u].push_back(s[v]+1);
}
if(fa&&vec[u].size()%2==0)vec[u].push_back(0);
else if(!fa&&vec[u].size()%2!=0)vec[u].push_back(0);
sort(vec[u].begin(),vec[u].end());
int siz=vec[u].size();
if(!fa)
{
for(int i=0;i<siz/2;i++)
if(vec[u][i]+vec[u][siz-i-1]<x)
return false;
}
else
{
int ans=0;
if(!check2(u,0,x))return false;
int l=0,r=siz-1;
while(l<=r)
{
int mid=(l+r)>>1;
if(check2(u,mid,x))ans=mid,l=mid+1;
else r=mid-1;
}
s[u]=vec[u][ans];
}
return true;
}
int main()
{
int n;
cin>>n;
for(int i=1;i<n;i++)
{
int u,v;
cin>>u>>v;
e[u].push_back(v);
e[v].push_back(u);
}
int l=1,r=n-1,ans=0;
while(l<=r)
{
int mid=(l+r)>>1;
memset(s,0,sizeof(s));
if(check(1,0,mid))ans=mid,l=mid+1;
else r=mid-1;
}
cout<<ans<<endl;
return 0;
}