CF1042F Leaf Sets

题意

给你一棵树,要求你把所有叶子分成一些集合

定义一个集合的代价为集合中距离最远点对的距离

给出 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\n", ans + 1);
	return 0;
}
posted @ 2018-09-18 10:48  EvalonXing  阅读(222)  评论(0编辑  收藏  举报