题意简述
给你一颗树有n(n<=5e4)个点,边权<=1e4。请你选出m(1<=m<n)条链,没有公共边,允许有公共点,使得 m m m条链的边权和的最小值最大。
思路
二分+树上贪心检验
具体思路
首先二分是显然的,“最小值最大”是特点,而且显然有单调性。
关键在于,我们钦定了最小值mid之后,如何检验。其实我们只要能找出>=m条链使得最小值>=mid即珂。我们需要贪心。
(令根节点为1)
贪心策略
假设现在考虑以 u u u为根的子树。对于 u u u的每个儿子 v v v,我们选择某一条到叶子节点的不带拐弯的链(后面会讲怎么选)。这条链的长度上传给 u u u节点。此外,这个长度还要再加上 u u u到 v v v的边权长。设这个长度为val[v]。
那么,有两种情况我们能选出来一个满足条件的链:
- val[v]>=k,这条链不带拐弯
- 存在v1和v2使得val[v1]+val[v2]>=k,带一个拐弯
对于情况1,直接ans++即珂。
对于情况2,我们把除了情况1以外的val值放到一个multiset里面,记为s[u]。然后我们每次拿出最小的(即*s[u].begin()
)为v1,找到一个最小的和它不一样的为v2,使得val[v1]+val[v2]>=k。
如果能找到,先记录答案ans++,然后把这两个从s[u]中删除。
如果找不到怎么办呢?说明这个val即不满足val[v]>=k,也没有v2使得val[v1]+val[v2]>=k。那么,这个val就只能上传给u的父亲。如果最后s[u]中只剩下一个珂供选择,也是同样的道理,要作为选择上传给 u u u的父亲。
在所有选剩下的节点中,我们显然要选长度最大的那一个给父亲。因为长度越大,越有珂能满足1和2条件中的一个。
简略证明正确性(解决几个小问题)
- 会不会有一个val[v1],能找到匹配,但是直接上传到 u u u的父亲比找一个 v 2 v2 v2匹配合算呢?
答案是不会。因为我们直接上传对答案的贡献也许是1(也许没有),还浪费了一个良好的匹配,也许以后就会匹配不上而导致答案不优;但是找匹配的话,不仅能弄一个1出来,匹配数和上面也是一样的。那就肯定更优。所以,我们的总体策略是对的
- 匹配的问题:取val最小的,然后lowerbound找匹配,这样一定最优吗?
因为我们还要让失配的上传给 u u u的父亲,而且要尽量大。所以我们肯定是省着点用,满足条件的里面选最小的即珂,这样能使留下来的最大,给父亲的就更有珂能找到更多满足条件的链。
代码
#include <bits/stdc++.h>
using namespace std;
namespace Flandre_Scarlet
{
#define N 155555
#define F(i,l,r) for(int i=l;i<=r;++i)
#define D(i,r,l) for(int i=r;i>=l;--i)