zoukankan      html  css  js  c++  java
  • [NOIP2018]赛道修建

    闲来无事,特意回味一下去年担当NOIPD1T3(防AK却没能防住)之大任的经典好题。

    首先看到什么“最短赛道的长度尽可能大”,就知道离不开二分。于是我们想到了一种思路:二分+树上贪心。

    二分判定很简单,就是对于二分出来的答案mid,检查树上是否有大于m条不相交的,权值和大于mid的链。而如何凑出尽可能多的权值和大于mid的链并使得它们互不相交,成为了这道题的关键之处。

    树上贪心。

    贪心策略不是很容易设计,下面给出一种:据题意可知,赛道的构建方式有且只有两种,1)一条链直接作为赛道(如下图中类似于红色的构建方式),2)两条半链(半链的理解对于这个题十分重要,请务必好好理解!)拼成一个赛道(因为不能走回头路因此最多只能两条拼)。我们对于以i为根的子树,先尽可能多的拼出赛道,同时在剩下拼不成整条赛道的情况下,选尽量长的传给父节点,作为父节点半链的候选答案。

    根据题意及描述以及样例,易得这是一棵树,我们可以把1号节点作为根,从它开始进行搜索,对于每一个节点i,按照上面的贪心策略进行处理。

    同时我们又会遇到一个问题:如何处理半链的拼接呢?显然,当子节点的最优长度加上本条边的长度>=Mid时,直接拼就可以了,如果不能的话,就把它放到multiset中进行维护,二分寻找最短的与当前长度的半链可以拼成整链的半链,如果找不到就打擂台取最大值,把最大的传上去即可。之后我们就可以开心地玩STL了~~~STL是很方便的,如果不会的同学应该尽快学习,尤其对于set,map,vector等写起来难但却非常实用的数据结构,STL简直是不二之选~!!!吹爆STL!!!

    下面给出两种不同数据结构的参考代码:

    vector(常数贼大,洛谷只能得85分)

     1 // luogu-judger-enable-o2
     2 #include<iostream>
     3 #include<algorithm>
     4 #include<cstdio>
     5 #include<vector> 
     6 #define N 1000005
     7 using namespace std;
     8 int read()
     9 {
    10     int x=0,f=1;char ch=getchar();
    11     while(ch<'0'||ch>'9'){if(ch=='-')f=0;ch=getchar();}
    12     while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
    13     if(f)return x;return -x;
    14 }
    15 int n,m,x,y,z,v[N],w[N],head[N],nxt[N],cnt,l=1,r=1e9,mid,sum;
    16 vector<int>e[N];
    17 void add(int a,int b,int c)
    18 {
    19     v[++cnt]=b;
    20     w[cnt]=c;
    21     nxt[cnt]=head[a];
    22     head[a]=cnt;
    23 }
    24 vector<int>::iterator it;
    25 int dfs(int x,int fa)
    26 {
    27     e[x].clear();
    28     for(int i=head[x];i;i=nxt[i])
    29     {
    30         int y=v[i];
    31         if(y==fa)continue;
    32         int val=w[i]+dfs(y,x);
    33         if(val>=mid)sum++;
    34         else e[x].push_back(val);
    35     }
    36     sort(e[x].begin(),e[x].end());
    37     int r=0;
    38     while(!e[x].empty())
    39     {
    40         if(e[x].size()==1)return max(r,*e[x].begin());
    41         it=lower_bound(e[x].begin(),e[x].end(),mid-*e[x].begin());
    42         if(it==e[x].begin())it++;
    43         if(it==e[x].end()){r=max(r,*e[x].begin());e[x].erase(e[x].begin());}
    44         else{sum++;e[x].erase(it);e[x].erase(e[x].begin());}
    45     }
    46     return r;
    47 }
    48 bool check()
    49 {
    50     sum=0;dfs(1,0);
    51     //cout<<mid<<" "<<sum<<endl;
    52     return sum>=m;
    53 }
    54 int main()
    55 {
    56     n=read();m=read();
    57     for(int i=1;i<n;i++)
    58     {
    59         x=read();y=read();z=read();
    60         add(x,y,z);add(y,x,z);
    61     }
    62     while(l<=r)
    63     {
    64         mid=(l+r)>>1;
    65         if(check())l=mid+1;
    66         else r=mid-1;
    67     }
    68     cout<<l-1<<endl;
    69     return 0;
    70 }
    vector85分代码

    multiset(可AC

     1 // luogu-judger-enable-o2
     2 // luogu-judger-enable-o2
     3 #include<iostream>
     4 #include<algorithm>
     5 #include<cstdio>
     6 #include<vector> 
     7 #include<set>
     8 #define N 1000005
     9 using namespace std;
    10 int read()
    11 {
    12     int x=0,f=1;char ch=getchar();
    13     while(ch<'0'||ch>'9'){if(ch=='-')f=0;ch=getchar();}
    14     while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
    15     if(f)return x;return -x;
    16 }
    17 int n,m,x,y,z,v[N],w[N],head[N],nxt[N],cnt,l=1,r=1e9,mid,sum;
    18 multiset<int>e[N];
    19 void add(int a,int b,int c)
    20 {
    21     v[++cnt]=b;
    22     w[cnt]=c;
    23     nxt[cnt]=head[a];
    24     head[a]=cnt;
    25 }
    26 multiset<int>::iterator it;
    27 int dfs(int x,int fa)
    28 {
    29     e[x].clear();
    30     for(int i=head[x];i;i=nxt[i])
    31     {
    32         int y=v[i];
    33         if(y==fa)continue;
    34         int val=w[i]+dfs(y,x);
    35         if(val>=mid)sum++;
    36         else e[x].insert(val);
    37     }
    38     //sort(e[x].begin(),e[x].end());
    39     int r=0;
    40     while(!e[x].empty())
    41     {
    42         if(e[x].size()==1)return max(r,*e[x].begin());
    43         it=e[x].lower_bound(mid-*e[x].begin());
    44         if(it==e[x].begin()&&e[x].count(*it)==1)it++;
    45         if(it==e[x].end()){r=max(r,*e[x].begin());e[x].erase(e[x].find(*e[x].begin()));}
    46         else{sum++;e[x].erase(e[x].find(*it));e[x].erase(e[x].find(*e[x].begin()));}
    47     }
    48     return r;
    49 }
    50 bool check()
    51 {
    52     sum=0;dfs(1,0);
    53     //cout<<mid<<" "<<sum<<endl;
    54     return sum>=m;
    55 }
    56 int main()
    57 {
    58     n=read();m=read();
    59     for(int i=1;i<n;i++)
    60     {
    61         x=read();y=read();z=read();
    62         add(x,y,z);add(y,x,z);
    63     }
    64     while(l<=r)
    65     {
    66         mid=(l+r)>>1;
    67         if(check())l=mid+1;
    68         else r=mid-1;
    69     }
    70     cout<<l-1<<endl;
    71     return 0;
    72 }
    100~~~

  • 相关阅读:
    HDU 1097 a hard puzzle
    HDU 4588 Count The Carries
    不想用锐捷怎么办?锐捷出问题|锐捷不能用怎么办?用menohust代替吧
    线段树及其变种的相关资料(不定期更新)
    UVa 10075
    UVa 1301
    UVa 10256
    UVa 1453
    计算几何相关资料+题目推荐(不定期补充)
    UVa 11524
  • 原文地址:https://www.cnblogs.com/szmssf/p/11261198.html
Copyright © 2011-2022 走看看