【洛谷P5021】[NOIP2018 提高组] 赛道修建

前言

\(STL\) 毁我青春!!!
【题目传送门】
被折磨之后发的求助讨论:【讨论传送门】

题解

分析做法

二分答案显然,关键在于 check()
根据每个节点 只有一个父亲,所以一旦某个节点选择向父亲选边,那么它的子树内的边都无法继续向上延伸,只能在子树内部消化了(在子树内部选出路径)。
根据贪心,如果在子树内能选出路径,就不需要考虑把这条路径保留,所以问题变为子树内的价值两两配对,最后剩下的价值最大值传递给父亲节点,可以用 set 或者 vector 实现。(set 应该复杂度更真一点)
特别地,如果一个节点本身的价值(相当于边权化点权)就已经足够当前二分的 \(mid\),就直接 cnt++,不需要放入 set 里面。

一些问题

  • seterase 函数有返回值,返回值是删除元素的下一个位置,这样可以避免删除当前元素导致迭代器失效的问题。
  • 如果一个节点本身的价值(相当于边权化点权)就已经足够当前二分的 \(mid\),就直接 cnt++,不需要放入 set 里面。
    这个特判我一开始写在下面就 \(WA\) 了。

代码

#include<bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f,N = 5e4+10;
#define ll long long int
inline ll read()
{
    ll ret=0;char ch=' ',c=getchar();
    while(!(c>='0'&&c<='9')) ch=c,c=getchar();
    while(c>='0'&&c<='9') ret=(ret<<1)+(ret<<3)+c-'0',c=getchar();
    return ch=='-'?-ret:ret;
}
int n,m;
int head[N],ecnt=-1;
struct edge
{
    int nxt,to,w;
}a[N<<1];
void init_edge(){memset(head,-1,sizeof(head)),ecnt=-1;}
inline void add_edge(int x,int y,int w)
{
    a[++ecnt]=(edge){head[x],y,w};
    head[x]=ecnt;
}
int cnt,dp[N];
multiset<int> s[N];
multiset<int>::iterator it1,it2;
inline void clear()
{
    memset(dp,0,sizeof(dp));
    cnt=0;
}
void dfs(int u,int fa,int mid)
{
    for(int i=head[u];~i;i=a[i].nxt)
    {
        int v=a[i].to;
        if(v==fa) continue;
        dfs(v,u,mid);
        if(dp[v]+a[i].w>=mid) cnt++;//发现问题,必须在这里特判,不能在下面特判
        else s[u].insert(dp[v]+a[i].w);
    }
    //printf("u=%d\n",u);
    for(it1=s[u].begin();it1!=s[u].end();)  
    {
    //  fprintf(stderr,"now=%d\n",*it1);        
        if(s[u].empty()) break;
        if(*it1>=mid) 
        {
            it1=s[u].erase(it1);
            cnt++;
            continue;
        }
        if((signed)s[u].size()<2) break;
        it2=s[u].lower_bound(mid-*it1);
        if(it2==s[u].end()) {it1++;continue;}//找不到
        if(it2==it1) it2++;//配对到自己,往后跳一个    
        if(it2==s[u].end()) {it1++;continue;}//只能找到自己(也是找不到)
    //  fprintf(stderr,"pair:(%d,%d)\n",*it1,*it2); 
        it1=s[u].erase(it1);
        if(it1==it2) it1=s[u].erase(it2),cnt++;
        else s[u].erase(it2),cnt++;
        if(it1==s[u].end()) break;
    }
    if(!s[u].empty()) 
    {
        it2=s[u].end(),it2--;
        dp[u]=*it2;
        s[u].clear();
    }
}
bool check(int mid)
{
    clear();
    dfs(1,-1,mid);
    return cnt>=m;
}
int main()
{
    #ifndef ONLINE_JUDGE
    freopen("build.in","r",stdin);
    freopen("build.out","w",stdout);
    #endif
    init_edge();
    n=read(),m=read();
    for(int i=1;i<n;i++)        
    {
        int u=read(),v=read(),w=read();
        add_edge(u,v,w),add_edge(v,u,w);
    }
    int l=1,r=1e9;
    while(l<r)
    {

        int mid=(l+r+1)>>1;
    //  fprintf(stderr,"mid=%d--------------------\n",mid);
        if(check(mid)) l=mid;
        else r=mid-1;
    }
    printf("%d\n",l);
    return 0;
}
posted @ 2021-11-01 16:48  conprour  阅读(59)  评论(0编辑  收藏  举报