zoukankan      html  css  js  c++  java
  • 洛谷 P5021 赛道修建(树上贪心)

    题目链接:https://www.luogu.com.cn/problem/P5021

    首先介绍一个东西:multiset。它可以看成一个有序的序列,且允许存在重复的数,并支持logn的时间插入和删除。(其他:https://blog.csdn.net/sodacoco/article/details/84798621

    看到题面,首先想到的是二分答案。二分第m条的长度,判断的话就看是否至少存在有m条路径,使得路径长度大于等于mid。

    怎样每次找的都是尽可能大的呢?那就得在树上贪心。

    贪心策略:

    对于经过一个点的赛道的构建方式有且只有两种:

    1.一条链直接作为赛道(如下图中类似于红色的构建方式)。

    2.两条半链拼成一个赛道。

    因此可以对于以u为根的子树,先尽可能多的拼出赛道,同时在剩下拼不成整条赛道的情况下,选尽量长的传给父节点,作为父节点半链的候选答案。

    如何处理半链的拼接呢?把它放到multiset中进行维护,二分寻找最短的与当前长度的半链可以拼成整链的半链,如果找不到就一直取最大值,把最大的传上去即可。

    AC代码(O2):

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<set>
     4 #include<cstring>
     5 using namespace std;
     6 int n,m,tot,num;
     7 const int N=50005;
     8 multiset<int> st[N];
     9 multiset<int>::iterator it1,it2;
    10 struct node{
    11     int to,next,w;
    12 }edge[N<<1];
    13 int head[N];
    14 void add(int u,int v,int w){
    15     edge[tot].to=v;
    16     edge[tot].next=head[u];
    17     edge[tot].w=w;
    18     head[u]=tot++;
    19 }
    20 int DFS(int u,int fa,int x,int dis){
    21     st[u].clear();
    22     for(int i=head[u];i!=-1;i=edge[i].next){
    23         int v=edge[i].to;
    24         if(v==fa) continue;
    25         int w=DFS(v,u,x,edge[i].w);
    26         if(w>=x) num++;
    27         else st[u].insert(w);
    28     }
    29     int maxx=0;
    30     if(st[u].size()==0) return dis;
    31     while(st[u].size()){
    32         it1=st[u].begin();
    33         int w=*it1;
    34         st[u].erase(it1);
    35         it2=st[u].lower_bound(x-w);
    36         if(it2!=st[u].end()){
    37             num++;
    38             st[u].erase(it2);
    39         }
    40         else maxx=max(maxx,w);
    41     }
    42     return dis+maxx;
    43 }
    44 bool check(int x){
    45     num=0;
    46     DFS(1,0,x,0);
    47     return num>=m;
    48 }
    49 int main(){
    50     memset(head,-1,sizeof(head));
    51     scanf("%d%d",&n,&m);
    52     for(int i=1;i<n;i++){
    53         int u,v,w;
    54         scanf("%d%d%d",&u,&v,&w);
    55         add(u,v,w); add(v,u,w);
    56     }
    57     int l=1,r=1e9;
    58     while(l<r){
    59         int mid=(l+r+1)>>1;
    60         if(check(mid)) l=mid;
    61         else r=mid-1;
    62     }
    63     printf("%d",l);
    64     return 0;
    65 }
    AC代码(O2)
  • 相关阅读:
    23 数字时钟&长图滚动
    22 日期特效&长图滚动
    彻底澄清c/c++指针概念
    已管理员模式运行批处理路径丢失问题的解决方法
    使用mathjax在博客中完美显示数学公式,支持PC,手机浏览器
    GOOGLE高级搜索技巧
    我要搬家
    简单的3proxy配置
    AutoMapper小结
    专业IT培训机构-传智播客
  • 原文地址:https://www.cnblogs.com/New-ljx/p/13847833.html
Copyright © 2011-2022 走看看