[NOIP2018]赛道修建

江枫思渺然 提交于 2021-01-04 08:02:21

闲来无事,特意回味一下去年担当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~~~

 

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!