【NOIP2018】赛道修建

本人NOIP的考试题……也是我NOIP2018唯一爆零的题目,正因如此,本人无缘NOIP2018一等奖。

少年醒醒,都9012年了,还搞什么啊,赶紧想正解……

本题的结构是一个树形的,因此我们可以用树形结构的相关知识解答。

本题的题目为“最小值最大”显然是二分答案的提示,因此我们二分答案最短的一条路径mid,在满足这个条件下判断是否能找出m条道路即可。

我们这样思考:在以i为根的子树内部,可以拼凑若干条道路(也可能没有),同时,我们选择一条不能拼出道路,且经过i点,而且最长的一条路径传给他的父亲,为他的父亲拼凑路径做贡献。

因此,我们对于每一个点都开一个multiset,对于每个结点,把所有传上来的值 val 放进一个 multiset ,其实这些值对答案有贡献就两种情况:

val≥k
vala+valb≥k
那么第一种情况可以不用放进 multiset,直接答案 +1 即可。第二种情况就可以对于每一个最小的元素,在 multiset 中找到第一个 ≥mid的数,拼成一条合法发路径,将两个数同时删去,最后把剩下最大的值传到那个结点的父亲

为什么这种解法是正确的?有没有可能对于有些情况直接传最大的数会使答案更大?

当然不会。这个数即使很大也只能对答案贡献加 1,在其没传上去的时候可以跟原来结点的值配对,也只能对答案贡献加 1

这就完成了我NOIP未了的心愿……

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <algorithm>
 5 #include <set>
 6 using namespace std;
 7 inline int read() {
 8     int ret=0;
 9     int op=1;
10     char c=getchar();
11     while(c<'0'||c>'9') {if(c=='-') op=-1; c=getchar();}
12     while(c<='9'&&c>='0') ret=ret*10+c-'0',c=getchar();
13     return ret*op;
14 }
15 struct node {
16     int next,to,dis;
17 }a[50010<<1];
18 int n,m,head[50010<<1],num,l=2147483647,r,sum;
19 multiset<int> s[50010];
20 multiset<int>::iterator it;
21 inline void add(int from,int to,int dis) {
22     a[++num].next=head[from];
23     a[num].to=to;
24     a[num].dis=dis;
25     head[from]=num;
26 }
27 inline int zhijing(int now,int fa) {
28     int sum1=0,sum2=0;
29     for(int i=head[now];i;i=a[i].next)
30         if(a[i].to!=fa) {
31             sum2=max(sum2,zhijing(a[i].to,now)+a[i].dis);
32             if(sum2>sum1) swap(sum2,sum1);
33         }
34     r=max(sum1+sum2,r);
35     return sum1;
36 }
37 int dfs(int now,int fa,int minn) {
38     int ret=0;
39     s[now].clear();
40     for(int i=head[now];i;i=a[i].next) {
41         int v=a[i].to;
42         if(v==fa) continue ;
43         int val=dfs(v,now,minn)+a[i].dis;
44         if(val>=minn) sum++;
45         else s[now].insert(val);
46     }
47     while(!s[now].empty()) {
48         if(s[now].size()==1) return max(ret,*s[now].begin());
49         it=s[now].lower_bound(minn-*s[now].begin());
50         if(it==s[now].begin()&&s[now].count(*it)==1) it++;
51         if(it==s[now].end()) {
52             ret=max(ret,*s[now].begin());
53             s[now].erase(s[now].find(*s[now].begin()));
54         }
55         else {
56             sum++;
57             s[now].erase(s[now].find(*it));
58             s[now].erase(s[now].find(*s[now].begin()));
59         }
60     }
61     return ret; 
62 }
63 inline bool check(int val) {
64     sum=0;
65     dfs(1,0,val);
66     return sum>=m;
67 }
68 int main() {
69     n=read(); m=read();
70     for(int i=1,x,y,z;i<n;i++) {
71         x=read(); y=read(); z=read();
72         add(x,y,z);
73         add(y,x,z);
74         l=min(l,z);
75     }    
76     zhijing(1,0);
77     int ans=0;
78     while(l<=r) {
79         int mid=(l+r)>>1;
80         if(check(mid)) {
81             ans=mid;
82             l=mid+1;
83         }
84         else r=mid-1;
85     }
86     printf("%d\n",ans);
87     return 0;
88 }
AC Code

 

转载于:https://www.cnblogs.com/shl-blog/p/10769485.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值