首页 > 其他分享 >luogu5021题解

luogu5021题解

时间:2024-02-20 21:44:23浏览次数:31  
标签:符合要求 int 题解 路径 luogu5021 长度 te 节点

形式化题意:在一棵树上找 \(m\) 条没有重复边的路径,使得最短的路径最大,求这个最短路径的最大值。

看到这个最大值就想二分答案。

\(1\le m\le n-1\),我们可以将长度的下限为最短的那条边,此时所有边都是符合要求的路径。
长度的上限为所有路径的长度除以 \(m\),向下取整。

我们在判断这个长度是否可以找出 \(m\) 条路径的过程中,用一个贪心的方法来使选取的边最多,然后判断边的数量是否足够即可。

当一棵树为菊花树时,我们令度最大的点为根节点,可以发现路径一定是根节点到子节点的一条边或是两条边拼起来。
一条边的情况需要该边大于等于要求的长度,满足这个条件的边没必要和其他边拼起来,直接作为一条路径即可。
除去大于等于要求长度的边后,剩下的边都不构成路径或是两条边的情况。
为使求得的路径最多,对于每条边我们都尽可能选择可以组成符合要求的路径但长度尽量小的边,因为长度大的边可能可以与更小的边组成符合要求的路径,但长度小的边可能不可以。
我们可以证明在菊花图上以任何顺序选取边并采取贪心策略的正确性。证明太长了拿出来水篇闲话(

如果该树不是菊花树时,会有非根的非叶节点。
此时可以有一条边与返祖的边连成路径。
不过与返祖边连成路径最多会使得路径多一条,在某节点各个向下的边连成路径的方案一定不劣于与返祖边连成路径。所以我们在某节点统计完路径选取剩下的边中最长的一边与上面的边连成路径即可。
此过程需要所在节点与子节点的边中从长度小的边开始选取,使得选取的与返祖边组成路径的边最长。
被选择的这条边在父节点可以将父节点与该子节点的边连起来,看作一条新边。
这样我们遍历一遍就可以解决这个问题了。

在一个节点统计路径与剩余最长路径的过程中和这篇博客类似,先将所有的边(我们把子节点遍历后返回的最长路径与该节点到子节点的边看作这里的边)排序,然后用 \(O(n)\) 的方法扫了一遍。
对于原本就大于等于 \(mid\) 的边直接累加进符合要求路径数中。

剩下的边从小的开始选,将符合条件的边由大到小入栈,这个过程用双指针实现,若栈非空则小的边与栈中的边合起来,弹栈并使符合要求路径数加 \(1\),否则更新向上返回的路径长度。

最后可能还有一些边留着栈中,由于这些边可以比其更小的边拼成一个符合要求的路径,所以其中任意两条边都可以拼成符合要求的路径,我们两个两个的弹栈并使符合要求路径数加 \(1\),最后如果还有边就更新向上返回的路径长度。

时间复杂度受排序所限为 \(O(n\log n)\),不过常数比 std::multiset 要小。

照着题解写的 std::multiset代码确实是慢了一些。
代码如下。

#include<bits/stdc++.h>
using namespace std;
#define pb push_back
#define fi first
#define se second
typedef pair<int,int> pii;
constexpr int MAXN=5e4+10;
int n,m,mid;
vector<pii> e[MAXN];
void add(int u,int v,int w){
    e[u].pb({v,w});
}
pii dfs(int u,int fa){
    int retfi=0,retse=0;
    vector<int> te;
    for(pii i:e[u]){
        if(i.fi==fa)continue;
        pii temp=dfs(i.fi,u);
        retfi+=temp.fi;
        int w=i.se+temp.se;
        if(w>=mid)++retfi;
        else te.pb(w);
    }
    sort(te.begin(),te.end());
    int pf=0,pl=te.size()-1;
    stack<int> s;
    while(pf<=pl){
        while(pf<pl&&te[pl]+te[pf]>=mid){
            s.push(te[pl]);--pl;
        }
        if(!s.empty()){
            ++retfi;s.pop();
        }else retse=te[pf];
        ++pf;
    }
    while(s.size()>=2){
        ++retfi;s.pop();s.pop();
    }
    if(!s.empty())retse=s.top();
    return {retfi,retse};
}
bool check(){
    return dfs(1,0).fi>=m;
}
int main(){
    scanf("%d%d",&n,&m);
    int tot=0;
    for(int i=1,a,b,l;i<n;++i){
        scanf("%d%d%d",&a,&b,&l);
        add(a,b,l);add(b,a,l);tot+=l;
    }
    int l=1,r=tot/m;
    while(l<r){
        mid=(l+r+1)>>1;
        if(check()){
            l=mid;
        }else{
            r=mid-1;
        }
    }
    printf("%d\n",l);
    return 0;
}

标签:符合要求,int,题解,路径,luogu5021,长度,te,节点
From: https://www.cnblogs.com/LiJoQiao/p/18024116

相关文章

  • 2024年2月20号题解
    P5594【XR-4】模拟赛解题思路重点是怎么判断是不是同一套模拟题用一个数组来标记是不是同一套题代码实现#define_CRT_SECURE_NO_WARNINGS#include<stdio.h>#include<stdlib.h>#include<string.h>#include<math.h>#include<time.h>#include<stdbool.h>#defi......
  • B3893 [NICA #3] 一键三连 题解
    本文同步发布于洛谷博客水题啊,我发现chen_zhe上传的水题这么多呢?难道kkksc03把水题都交给他上传了?题意简述输入两个整数\(x\)和\(y\),输出\(x+y\times2\)。我能直接上代码吗好的,让我们通过做题认识一下相关的知识点:YF001int类型与输入输出YF002数与数之间的运......
  • 线段树-山海经 题解
    《最痛苦的一集》从开始的找维护变量到依据i比较依据y比较最后发现问题出在没初始化(如果不将答案赋值为极小值那么它最小值就是0因此wa了无数遍下面是思路首先要维护的变量有:\(区间的左右边界\,l,\,r\)\(区间的答案\,ans\)\(含左端点最大值\,lans\,和含右端点最......
  • 【解题报告】【比赛复现】洛谷入门赛 #17 题解
    洛谷入门赛#17题解今日推歌:《春嵐feat.初音ミク》john感觉这首都快成周榜战神了(Before关于我做入门赛的精神状态:没做T4,因为题面读得我头疼……而且大模拟不想做(虽然也不是多大的模拟展开目录目录洛谷入门赛#17题解BeforeA食堂B数学选择题AfterC风球E式神考核Af......
  • CF1905D Cyclic MEX 题解
    题意:给定一个长度为\(n\)的排列\(a\),\(a_i\in[0,n-1]\)。你可以将这个排列进行循环移位,最小化\(\sum_{i=1}^{n}\text{mex}_{j=1}^ia_j\)的值。首先我们可以先计算出最初情况下每一个\(i\)位置的\(\text{mex}\)值。这个序列一定是单调非严格递增的。首先有一个比......
  • CF1893B Neutral Tonality 题解
    很巧妙的一道题。为了让\(\text{LIS}\)长度最小,我们肯定先将\(b\)数组降序排序,这样\(b\)自身对\(\text{LIS}\)的贡献最小。考虑是否存在一种插入方式使得最终\(a\)的\(\text{LIS}\)长度和最初\(a\)的\(\text{LIS}\)长度相等。这时我们会发现,如果我们插入\(b\)......
  • CF638C题解
    我们可以针对一个顶点只能同时修一条边这个条件设计方案。由于每条边都要修一遍,同样某天修理的方案放在哪一天修都无所谓,我们采用贪心的策略,在原有的方案上尽可能多地修边。根据上面的性质,我们只需要将同一顶点的边放在不同的某天修理的方案中,使方案尽可能的少即可。跑一遍dfs......
  • Docker 使用遇到的问题解决 更改Tag
    dockertagconsul:1.15.4consul:latestdockerrmiconsul:1.15.4删除制定版本在运行时,有些镜像拉取时报错我这里 时 consu,只能制定版本下载1.15.4Errorresponsefromdaemon:manifestforconsul:latestnotfound:manifestunknown:manifestunknown ......
  • P2171 Hz吐泡泡 题解
    传送门题目背景Hz大大是一种可爱的动物(神)。他很喜欢吐泡泡(更喜欢写作业)。题目描述这天,Hz大大心血来潮,吐了n个不同的泡泡玩(保证没有重复的泡泡)。因为他还要写作业,所以他请你帮他把这些泡泡排序成树(左子树<=根<右子树)。输出它的后序遍历。输入格式共2行。第一行,1个整数n。(1<......
  • 洛谷P10179 题解
    题意简述给定\(n\)个点,要求构造出一棵树,同时有\(m\)个事件,第\(i\)个事件要求\(u_i\)和\(v_i\)用两条树边连接,即当中相隔一个点。若存在构造方案,输出Yes并输出其中一种方案,否则输出No。思维路径首先简化问题,假如我们想让一堆点互相相隔一个点,我们的做法。考虑菊花......