题解 P6142 【[USACO20FEB]Delegation P】

· · 题解

题解 - \mathrm{P6142 \ [USACO20FEB] \ Delegation\ P}

\huge\color{blue}\boxed{\color{Violet}\mathfrak{There\ is \ my \ blog}}

题目意思

\mathrm{Sol}

\mathrm{Code}

#include <bits/stdc++.h>
#define pb push_back
using namespace std;

inline int read()
{
    int sum=0,ff=1; char ch=getchar();
    while(!isdigit(ch))
    {
        if(ch=='-') ff=-1;
        ch=getchar();
    }
    while(isdigit(ch))
        sum=sum*10+(ch^48),ch=getchar();
    return sum*ff;
}

const int N=1e5+5;

int n,m,ans,flg,jb,f[N];
vector<int> G[N];

inline void dfs(int u,int fa,int L)
{
    multiset<int> M;//multiset容器
    if(flg) return;
    for ( int i=0;i<G[u].size();i++ )
    {
        int v=G[u][i];
        if(v==fa) continue;
        dfs(v,u,L);
        M.insert(f[v]+1);
    }
    jb=0;
    if((u==1&&M.size()&1)||(u!=1&&!(M.size()&1))) M.insert(0);
    //强制除根节点外的点从儿子连边过来的边数为奇数条
    while(M.size())
    {
        multiset<int>::iterator It=M.begin();//取当前最小边
        int small=*It;
        M.erase(It);
        multiset<int>::iterator Big=M.lower_bound(L-small);
        //二分找到第一条大于等于二分的L的边
        if(u==1) 
        {
            if(Big==M.end())//找不到,走人
            {
                flg=1;
                break;
            }
            M.erase(Big);
        }
        else 
        {
            if(Big==M.end()&&jb)
            {
                flg=1;
                break;
            }
            if(Big==M.end()&&!jb) 
                jb=1,f[u]=small;//找到那一条可以给f[u]造成贡献的边
            if(Big!=M.end()) M.erase(Big);
        }
    }
}

inline bool check(int mid)
{
    flg=0;
    memset(f,0,sizeof f);
    dfs(1,0,mid);
    return (!flg);
}

int main()
{
    n=read();
    for ( int i=1;i<n;i++ )
    {
        int x,y;
        x=read(),y=read();
        G[x].pb(y);
        G[y].pb(x);
    }
    int l=0,r=n;
    while(l<=r)
    {
        int mid=(l+r)/2;
        if(check(mid)) 
            l=mid+1,ans=mid;
        else r=mid-1;//二分答案
    }
    printf("%d\n",ans);
    return 0;
}