[luogu]P5021 赛道修建

原题链接:P5021 赛道修建

题意

给定一颗树(不是二叉树),在树上找到$m$条链。

求最短链的最大值。

分析

其实在考场上想出正解了的。。

就是因为不会STL结果挂了。。

要是写出来就1=了呢。。

气死了。

 

 

首先因为要求最短链的最大值,很容易想到二分。

二分出最短链的长度$x$,然后要在树上找到$m$条长度大于$x$的链。

分析问题,每条边只能用一次,然后可以发现每个点到他的父亲点,只能有一条链向上传递,其他的链只能互相组合。

很简单的贪心,我们要让这条向上传递的链最大,其他的链组合成尽量多的链,使组合成的链大于$x$。

如果一条链本身就大于$x$,我们直接在总链数上累加。

不然记录下这条链。

我在考场上想的是,把所有的链记录下来,然后排序进行双指针。

这样好像是$O(n^2logn)$的,而且实现巨烦。。

考场上打了一个半小时,最终没有AK DAY1。。。

 

回来看到网上的处理方法是用multiset。。

考场上怎么就没想到STL呢。。

把所有小于$x$的链丢到multiset里去,每次取出最小的链记为$y$,在链里面找出第一个大于等于$x-y$的链,然后同时删掉这两条链就可以了。

要注意集合里只有一条链的情况。

 

实现起来巨简单。。。

气死我了鸭……

代码

 1 #include <bits/stdc++.h>
 2 #define mid ((l+r)>>1)
 3 using namespace std;
 4 const int N=5e5+1009;
 5 int read(){
 6     char c;int num,f=1;
 7     while(c=getchar(),!isdigit(c))if(c=='-')f=-1;num=c-'0';
 8     while(c=getchar(), isdigit(c))num=num*10+c-'0';
 9     return f*num;
10 }
11 int n,m,fa[N],val[N],cnt;
12 int head[N],ver[N*2],nxt[N*2],edge[N*2],tot=1;
13 int dfs(int x,int v){
14     multiset<int>S;
15     multiset<int>::iterator it;
16     S.clear();
17     for(int i=head[x];i;i=nxt[i]){
18         if(ver[i]==fa[x])continue;
19         int val=dfs(ver[i],v)+edge[i];
20         if(val>=v)cnt++;
21         else S.insert(val);
22     }
23     int maxn=0;
24     while(!S.empty()){
25         if(S.size()==1)return max(maxn,*S.begin());
26         it=S.lower_bound(v-*S.begin());
27         if(it==S.begin()&&S.count(*it)==1)it++;
28         if(it==S.end()){
29             maxn=max(maxn,*S.begin());
30             S.erase(S.find(*S.begin()));
31         }else {
32             cnt++;
33             S.erase(S.find(*it));
34             S.erase(S.find(*S.begin()));
35         }
36     }
37     return maxn;
38 }
39 bool check(int x){
40     cnt=0;dfs(1,x);
41     if(cnt>=m)return 1;
42     else return 0;
43 }
44 void build(int x,int pre){
45     for(int i=head[x];i;i=nxt[i]){
46         if(ver[i]==pre)continue;
47         fa[ver[i]]=x;
48         build(ver[i],x);
49     }
50 }
51 void add(int u,int v,int w){
52     ver[++tot]=u;nxt[tot]=head[v];head[v]=tot;edge[tot]=w;
53     ver[++tot]=v;nxt[tot]=head[u];head[u]=tot;edge[tot]=w;
54 }
55 int main()
56 {
57     int l=0,r=0;
58     n=read();m=read();
59     for(int i=1;i<n;i++){
60         int u,v,w;
61         u=read();v=read();w=read();
62         add(u,v,w);r+=w;
63     }
64     build(1,0);
65     while(l<=r){
66         if(check(mid))l=mid+1;
67         else r=mid-1;
68     }
69     printf("%d\n",r);
70     return 0;
71 }
View Code

 

转载于:https://www.cnblogs.com/onglublog/p/10169614.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值