闲来无事,特意回味一下去年担当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 }
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 }
)
来源:oschina
链接:https://my.oschina.net/u/4349448/blog/3448118