题意
给你一棵树,要求你把所有叶子分成一些集合
定义一个集合的代价为集合中距离最远点对的距离
给出 k ,求在最大集合代价不超过 k 的前提下,划分出的最少集合数
先尝试列一些式子和性质(看一些题解), 发现:
当 depest[sx_i]+ depest[sx_j] +2 <= k 时,子树中的集合才可以合并成为一个集合
这很显然,但不把它列出来我并不能意识到拿它作为更新的条件
似乎可以设 f[x] = Σf[sx_i] 表示当前节点子树中的集合数,然后根据上边的式子更新一下合并后的 f[x]
这是可做的然而有点麻烦,主要在于有一些已经无法合并的集合,这需要额外乱搞
可以这样做:
每次向上更新的时候,不将无法合并的集合向上更新,只留下还可能被更新的,
这样只需要在 dfs 时返回剩下的集合直接计入答案即可
代码:
#include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cctype> #include<queue> using namespace std; const int MAXN = 1000005; struct EDGE{ int nxt, to; EDGE(int NXT = 0, int TO = 0) {nxt = NXT; to = TO;} }edge[MAXN << 1]; int n, k, totedge, Root = 1, ans; int head[MAXN], dpst[MAXN], deg[MAXN]; inline int rd() { register int x = 0; register char c = getchar(); while(!isdigit(c)) c = getchar(); while(isdigit(c)) { x = x * 10 + (c ^ 48); c = getchar(); } return x; } inline void add(int x, int y) { edge[++totedge] = EDGE(head[x], y); head[x] = totedge; ++deg[y]; return; } int dfs(int x, int frm) { if(deg[x] == 1) return 0; priority_queue<int> q; for(int i = head[x]; i; i = edge[i].nxt) if(edge[i].to != frm) { int y = edge[i].to; q.push(dfs(y, x) + 1); } int lst = q.top(); q.pop(); while(q.size() >= 1) { if(lst + q.top() <= k) return lst; ++ans; lst = q.top(); q.pop(); } return lst; } int main() { n = rd(); k = rd(); register int xx, yy; for(int i = 1; i < n; ++i) { xx = rd(); yy = rd(); add(xx, yy); add(yy, xx); } for(int i = 1; i <= n; ++i) if(deg[i] != 1) { Root = i; break; } dfs(Root, 0); printf("%d ", ans + 1); return 0; }