CF#507 Div1 D. You Are Given a Tree
? 解题记录 ? ? Codeforces ? ? 贪心 ? ? 乱搞 ? ? 打表 ?    2018-09-18 09:03:06    410    0    0
output
standard output

A tree is an undirected graph with exactly one simple path between each pair of vertices. We call a set of simple paths kk-valid if each vertex of the tree belongs to no more than one of these paths (including endpoints) and each path consists of exactly kk vertices.

You are given a tree with nn vertices. For each kk from 11 to nn inclusive find what is the maximum possible size of a kk-valid set of simple paths.

Input

The first line of the input contains a single integer nn (2n1000002≤n≤100000) — the number of vertices in the tree.

Then following n1n−1 lines describe the tree, each of them contains two integers vvuu (1v,un1≤v,u≤n) — endpoints of the corresponding edge.

It is guaranteed, that the given graph is a tree.

Output

Output nn numbers, the ii-th of which is the maximum possible number of paths in an ii-valid set of paths.

Examples
input
Copy
7
1 2
2 3
3 4
4 5
5 6
6 7
output
Copy
7
3
2
1
1
1
1
input
Copy
6
1 2
2 3
2 4
1 5
5 6
output
Copy
6
2
2
1
1
0
Note

One way to achieve the optimal number of paths for the second sample is illustrated in the following picture:

题意:给你一棵树,问用长度为k的链去覆盖它,链两两不能相交,最多能盖多少条链上去。

我们从简单的情况考虑,在一条链上对于每一个k答案会是n/k。并且我们注意到n/k的整出性质:只有sqrt(n)种取值,并且答案单调。我们大胆猜想,树上情况的取值也在sqrt(n)级别内,显然也是单调的。

如果我们可以对于k单点求值那么我们就可以记忆化+二分在sqrt(n)log(n)的额外复杂度下解决本题,那么问题在于如何对于k单点求值。

考虑贪心,每一次我们枚举最高点在u的链的放置方案,这样链的两端一定插在不同的儿子中。我们发现每一个儿子对父亲的贡献只有向下到达的最深点,因为任意两条链不能重叠。那么每一次找寻儿子中最深的两个,如果大于k那么就放置,否则更新当前u的最深处。总复杂度:O(n sqrt(n)log(n))

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 1e5 + 5;
struct edge {
	int v, next;
} e[maxn << 1];
int head[maxn], cnt, depth[maxn], dfn[maxn], dcnt, ans, fa[maxn];
int n, u, v, mem[maxn], _1st[maxn], _2nd[maxn], ord[maxn], B = 500;

void adde(const int &u, const int &v) {
	e[++cnt] = (edge) {v, head[u]};
	head[u] = cnt;
}

void dfs(const int &u, const int &p) {
	fa[u] = p;
	dfn[u] = ++dcnt;
	ord[dcnt] = u;
	for(register int i = head[u]; i; i = e[i].next) {
		int v = e[i].v;
		if(v == p) continue;
		dfs(v, u);
	}
}

int Calc(const int &k) {
	int p, u, ans = 0;
	memset(_1st, 0, sizeof(_1st));
	memset(_2nd, 0, sizeof(_2nd));
	for(register int i = n; i >= 1; --i) {
		u = ord[i], p = fa[u];
		if(!_1st[u]) depth[u] = 1;
		if(_2nd[u] + _1st[u] + 1 >= k) ++ans, depth[u] = 0;
		else depth[u] = _1st[u] + 1;
		if(depth[u] > _1st[p]) _2nd[p] = _1st[p], _1st[p] = depth[u];
		else if(depth[u] > _2nd[p]) _2nd[p] = depth[u];
	}
	return ans;
}

int DF(int l, int r, int ans) {
	while(l < r - 1) {
		int mid = l + r >> 1;
		if(mem[mid] == -1) 
			mem[mid] = Calc(mid);
		if(mem[mid] >= ans) l = mid;
		else r = mid;
	}
	return l;
}

int main() {
	memset(mem, -1, sizeof(mem));
	scanf("%d", &n);
	for(register int i = 1; i < n; ++i)
		scanf("%d%d", &u, &v), adde(u, v), adde(v, u);
	dfs(1, 0), mem[1] = n, B = min(B, n);
	for(register int i = 2; i <= B; ++i) 
		mem[i] = Calc(i);
	for(register int l = B + 1, r; l <= n; l = r + 1) {
		if(mem[l] == -1) mem[l] = Calc(l);
		r = DF(l, n + 1, mem[l]);
		for(register int i = l + 1; i <= r; ++i)
			mem[i] = mem[l];
	}
	for(register int i = 1; i <= n; ++i)
		printf("%d\n", mem[i]);
	return 0;
}

 

上一篇: CF#508 Div2 F. Wrap Around

下一篇: CF#502 Div1+Div2 G.The Tree

410 人读过
立即登录, 发表评论.
没有帐号? 立即注册
0 条评论
文档导航