[USACO 2011 Dec Gold] Cow Calisthenics【二分】

Problem 1: Cow Calisthenics [Michael Cohen, 2010]

Farmer John continues his never-ending quest to keep the cows fit
by having them exercise on various cow paths that run through the
pastures. These cow paths can be represented as a set of vertices
connected with bidirectional edges so that each pair of vertices
has exactly one simple path between them. In the abstract, their
layout bears a remarkable resemblance to a tree. Surprisingly, each
edge (as it winds its way through the pastures) has the same length.

For any given set of cow paths, the canny cows calculate the longest
possible distance between any pair of vertices on the set of cowpaths
and call it the pathlength. If they think this pathlength is too
large, they simply refuse to exercise at all.

Farmer John has mapped the paths and found V (2 <= V <= 100,000)
vertices, conveniently numbered from 1..V. In order to make shorter
cowpaths, he can block the path between any two vertices, thus
creating more sets of cow paths while reducing the pathlength of
both cowpath sets.

Starting from a single completely connected set of paths (which
have the properties of a tree), FJ can block S (1 <= S <= V-1)
paths, creating S+1 sets of paths. Your goal is to compute the best
paths he can create so that the largest pathlength of all those
sets is minimized.

Farmer John has a list of all V-1 edges in his tree, each described
by the two vertices A_i (1 <= A_i <= V) and B_i (1 <= B_i <= V; A_i
!= B_i) that it connects.

Consider this rather linear cowpath set (a tree with 7 vertices):

                   1---2---3---4---5---6---7

If FJ can block two paths, he might choose them to make a map like
this:
		   1---2 | 3---4 | 5---6---7

where the longest pathlength is 2, which would be the answer in
this case. He can do no better than this.

TIME LIMIT: 2 seconds

MEMORY LIMIT: 32 MB

PROBLEM NAME: exercise

INPUT FORMAT:

* Line 1: Two space separated integers: V and S

* Lines 2..V: Two space separated integers: A_i and B_i

SAMPLE INPUT (file exercise.in):

7 2
6 7
3 4
6 5
1 2
3 2
4 5

OUTPUT FORMAT:

* Line 1: A single integer that is the best maximum pathlength FJ can
        achieve with S blocks

SAMPLE OUTPUT (file exercise.out):

2

 

 

不懂英文自行解决。。。

一看到最大值最小,就是二分,可是想了半天也没想出来如何check。看了题解,恍然大悟。

随便选一个根拉成一棵树,然后对于以r为根的子树,假设以其儿子节点为根的子树中该断的边已经断了,那么对于以r为根的子树中,其直径为“最深”的两个儿子i与j的深度之和 + 2,“最深”的意思是,以这两个节点往下走能走的最深。如果这个值大于二分的那个最大值,则最深的那个儿子就要断开与r的连接,这样直到“最深”的两个儿子i与j的深度之和 + 2 <= 二分的最大值。当有一个节点时同理,反正剩下就是细节了。

 

#include <cstdio>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <algorithm>

const int maxn = 100005;

int n, s, t1, t2, ans_s, root, biaozhun;
int head[maxn], to[maxn << 1], next[maxn << 1], lb;
std::vector<int> son[maxn];

inline void ist(int aa, int ss) {
	to[lb] = ss;
	next[lb] = head[aa];
	head[aa] = lb;
	++lb;
}
bool cmp(int aa, int ss) {
	return aa > ss;
}
int dfs(int r, int p) {
	for (int j = head[r]; j != -1; j = next[j]) {
		if (to[j] != p) {
			son[r].push_back(dfs(to[j], r));
		}
	}
	std::sort(son[r].begin(), son[r].end(), cmp);
	if (son[r].size() > 1) {
		int lmt = son[r].size();
		int i;
		for (i = 0; i < lmt - 1; ++i) {
			if (son[r][i] + son[r][i + 1] + 2 <= biaozhun) {
				break;
			}
			++ans_s;
		}
		if (i == lmt - 1) {
			if (son[r][i] + 1 > biaozhun) {
				++ans_s;
				return 0;
			}
			else {
				return son[r][i] + 1;
			}
		}
		else {
			return son[r][i] + 1;
		}
	}
	else if (son[r].size() == 1) {
		if (son[r][0] + 1 > biaozhun) {
			++ans_s;
			return 0;
		}
		else {
			return son[r][0] + 1;
		}
	}
	else {
		return 0;
	}
}
inline bool check(int mx) {
	ans_s = 0;
	biaozhun = mx;
	for (int i = 1; i <= n; ++i) {
		son[i].clear();
	}
	dfs(root, 0);
	return ans_s <= s;
}

int main(void) {
	freopen("exercise.in", "r", stdin);
	freopen("exercise.out", "w", stdout);
	memset(head, -1, sizeof head);
	memset(next, -1, sizeof next);
	unsigned seed;
	scanf("%d%d", &n, &s);
	seed += n + s;
	for (int i = 1; i < n; ++i) {
		scanf("%d%d", &t1, &t2);
		seed += t1 + t2;
		ist(t1, t2);
		ist(t2, t1);
	}
	srand(seed);
	root = rand() % n + 1;
	
	int left = 0, right = n, mid;
	while (left != right) {
		mid = (left + right) >> 1;
		if (check(mid)) {
			right = mid;
		}
		else {
			left = mid + 1;
		}
	}
	printf("%d\n", left);
	return 0;
}

  

posted @ 2016-11-07 19:51  ciao_sora  阅读(262)  评论(0编辑  收藏  举报