1. 程式人生 > 實用技巧 >Luogu5021 賽道修建

Luogu5021 賽道修建

https://www.luogu.com.cn/problem/P5021

圖論

十分簡單的題目,想起NOIP2018時嘆其為不可做題,一點一點啃暴力分(自閉)

很明顯,需要二分答案,設二分到的值為\(mid\)

對於每個節點考慮,倘若有一條鏈經過節點\(u\)和其父親之間的連邊,那麼我們只能有一條鏈延伸上去,在滿足取得的鏈數最多的情況下,我們只需要讓延伸上去的鏈最長即可,設延伸上去的最長鏈長為\(mxl_u\)

怎麼操作?對於\(u\)的子節點\(v\),他們都存在一個\(mxl_v\),當然,\(mxl_v\)要加上\(edge_{u,v}\)之後進行操作,然後我們將這些數當成數列\(A\)

對於已經\(\ge mid\)

\(a_i\),直接單獨增加答案,然後去掉即可

我們從小到大匹配,對於\(a_i\),如果有\(\ge mid-a_i\)的數,直接匹配滿足條件的最小的那一個,答案增加,否則\(a_i\)作為\(mxl_u\)的候選

要支援動態刪除,可以直接利用\(multiset\),然後就\(AC\)

\(Code:\)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<set>
#define N 50005
using namespace std;
int n,m,x,y,z,tot,mxl[N],fr[N],nxt[N << 1],d1[N << 1],d2[N << 1];
int l=0,r=0,mid,ans=0,kz;
multiset<int>s;
void add(int x,int y,int z)
{
    tot++;
    d1[tot]=y;
    d2[tot]=z;
    nxt[tot]=fr[x];
    fr[x]=tot;
}
void dfs(int u,int F)
{
    for (int i=fr[u];i && kz>0;i=nxt[i])
    {
        int v=d1[i];
        if (v==F)
            continue;
        if (!nxt[fr[v]])
        {
            mxl[v]=d2[i];
            continue;
        }
        dfs(v,u);
        mxl[v]+=d2[i];
    }
    if (kz<=0)
        return;
    s.clear();
    mxl[u]=0;
    for (int i=fr[u];i;i=nxt[i])
    {
        int v=d1[i];
        if (v==F)
            continue;
        if (mxl[v]>=mid)
            kz--; else
            s.insert(mxl[v]);
    }
    while (!s.empty() && kz>0)
    {
        multiset<int> :: iterator it=s.begin();
        int o=*it;
        s.erase(it);
        multiset<int> :: iterator it2=s.lower_bound(mid-o);
        if (it2!=s.end())
        {
            kz--;
            s.erase(it2);
            continue;
        }
        mxl[u]=max(mxl[u],o);
    }
}
bool check()
{
    kz=m;
    dfs(1,0);
    if (mxl[1]>=mid)
        kz--;
    return kz<=0;
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<n;i++)
    {
        scanf("%d%d%d",&x,&y,&z);
        add(x,y,z),add(y,x,z);
        r+=z;
    }
    r/=m;
    while (l<=r)
    {
        mid=(l+r) >> 1;
        if (check())
        {
            ans=mid;
            l=mid+1;
        } else
            r=mid-1;
    }
    printf("%d\n",ans);
    return 0;
}