形式化题面如下:

给定一棵有nn 个节点的无向树和一个整数kk,选出最多kk 条不分叉的路径(即简单链),使得这些路径覆盖的不同节点数尽可能多。输出最多能覆盖的节点数。

DP 显然不太好,考虑贪心,那么贪心尽量让链长。考虑直径一定作为答案的一部分出现,而剩下的就是直径上的分支,分支跨直径配对成路径。考虑这个如何配对,其实就是不同链的叶子两两配对,考虑以直径一端点为根,长链剖分加排序(链长大到小)取2L12L-1 个叶子即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#include<bits/stdc++.h>
#define pir pair<int,int>
using namespace std;
constexpr int MN=1e6+1520;
int n,L,rt,ftot,ans;
pir lvf[MN];
bool vis[MN];
int hd[MN],nxt[MN<<1],to[MN<<1],tot;
void add(int u,int v){to[++tot]=v,nxt[tot]=hd[u],hd[u]=tot;}

namespace ZJTree{
struct Node{int u,fa,len;};
int bfs(int st){
queue<Node> q;
int ans1=-1e9,ans2=1;
q.push({st,0,0});
while(!q.empty()){
auto [u,fa,w]=q.front();q.pop();
if(w>ans1) ans1=w,ans2=u;
for(int i=hd[u];i;i=nxt[i]){
int v=to[i];
if(v==fa) continue;
q.push({v,u,w+1});
}
}
return ans2;
}
}

namespace Tree{
int dep[MN],fa[MN],mxdep[MN],htop[MN],len[MN],hson[MN];
void dfs1(int u,int pre){
fa[u]=pre;
dep[u]=mxdep[u]=dep[pre]+1;
for(int i=hd[u];i;i=nxt[i]){
int v=to[i];
if(v==pre) continue;
dfs1(v,u);
if(mxdep[u]<mxdep[v]) mxdep[u]=mxdep[v],hson[u]=v;
}
len[u]=mxdep[u]-dep[u]+1;
}
void dfs2(int u,int ltop){
htop[u]=ltop;
if(!hson[u]){lvf[++ftot]=pir(len[htop[u]],u);return;}
dfs2(hson[u],ltop);
for(int i=hd[u];i;i=nxt[i]){
int v=to[i];
if(v==fa[u]||v==hson[u]) continue;
dfs2(v,v);
}
}
}using namespace Tree;

bool cmp(pir x,pir y){return x.first>y.first;}

int main(){
read(n,L);
for(int i=1,u,v;i<n;i++) read(u,v),add(u,v),add(v,u);
rt=ZJTree::bfs(1);
dfs1(rt,0);
dfs2(rt,rt);
sort(lvf+1,lvf+1+ftot,cmp);
for(int i=1;i<=(L<<1)-1;i++){
if(i==1) vis[rt]=1,ans+=len[rt];
else{
int p=lvf[i].second;
while(!vis[htop[p]]) vis[htop[p]]=1,ans+=len[htop[p]],p=fa[htop[p]];
}
}
put(ans);
return 0;
}