CF1039D You Are Given a Tree

把一颗树分为多条长度为k的不交路径,求k 从1~n的最多条数

因为一个节点最多被使用一次,叶子只有一条被覆盖的情况(从父亲来),所以我们可以贪心的从叶子向上走,长度一到k就断掉。这样的贪心是正确的

可以发现,随着k的递增,答案是下降的。因此我们可以发现相同的答案都是成段的。我们可以想到二分,答案最多n种,最多二分n次,时间复杂度为(O(n^2 log n))

但是我们可以发现一棵n个节点树最多可以被分为n/k条,有点像整除分块。可以证明答案最多有(sqrt n)

所以实际的时间复杂度为(O(n sqrt n log n))

可能前面T个答案,答案段数比较多,T~n的答案段数比较少,所以我们可以根号分治

但是实际上还可以优化,我们可以将k<=T的直接暴力。如果二分的话,可能会更劣,因为可能会多个log ,大于k的二分,可以做到(O(n log n frac{n}{T} )),可以发现T取(sqrt{n log n})可以使得前面的暴力和后面的二分的时间复杂度和的最小(基本不懂式)。

const int N = 1e5 + 79;
struct graph {
	int head[N], tot, next[N << 1], ver[N << 1];
	inline void add(int a, int b) {
		ver[++tot] = b;
		next[tot] = head[a];
		head[a] = tot;
	}
} G;
int n, block;
int fa[N], b[N], tim;
int len[N];
inline void dfs(int x) {
	repg(x) {
		int y(G.ver[i]);
		if(y == fa[x]) continue;
		fa[y] = x;
		dfs(y);
	}
	b[++tim] = x;
}

inline int calc(int k) {
	int res(0);
	rep(i, 1, n) {
		len[i] = 1;
	}
	rep(i, 1, n) {
		int x = b[i];
		if(fa[x] && len[x] != -1 && len[fa[x]] != -1) {
			if(len[x] + len[fa[x]] >= k) {
				len[fa[x]] = -1;
				++res;
			} else {
				len[fa[x]] = max(len[fa[x]], len[x] + 1);
			}
		}
	}
	return res;
}
int ans[N];
int main() {
	read(n);
	int x, y;
	rep(i, 2, n) {
		read(x);
		read(y);
		G.add(x, y);
		G.add(y, x);
	}

	block =sqrt(n * log2(n));
	dfs(1);
	out(n, '
');
	rep(i, 2, block) {
		out(calc(i), '
');
	}
	int l, r, ans, t;
	for(int i = block + 1; i <= n; i = l + 1) {
		l = i;
		r = n;
		ans = calc(i), t = i;
		while(l <= r) {
			int mid(l + r >> 1);
			if(calc(mid) == ans) {
				l = mid + 1;
				t = mid;
			} else r = mid - 1;
		}
		rep(k, i, t) {
			out(ans, '
');
		}
		l = t;
	}
	return 0;
}

本文来自博客园,作者:{2519},转载请注明原文链接:https://www.cnblogs.com/QQ2519/p/15506025.html

原文地址:https://www.cnblogs.com/QQ2519/p/15506025.html