首页 > 代码库 > ZOJ 3820 Building Fire Stations

ZOJ 3820 Building Fire Stations

题意:

树上找两个点  使得其他点到这两点任意一点的距离的最大值最小

思路:

最大值最小  想到二分  在二分的基础上判定这个最大值是否可能

如何判定这个问题就是如何选那两个点的问题  很明显  我们要处理的是直径(不然没意义  最长的就是直径)  那么既然已经有了一个要判定的值x  不妨就选择距离直径两端点距离为x的点就好

直径上的点最多n个  算上二分的复杂度  O(nlogn)可以解决

代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
#include<set>
#include<vector>
#include<queue>
#include<cstdlib>
#include<ctime>
#include<cmath>
using namespace std;
typedef long long LL;
#define N 200010

int t, n;
struct edge {
	int v, next;
} ed[N * 2];
int head[N], tot;
int dis[N], pre[N];
int route[N], m;

void add(int u, int v) {
	ed[tot].v = v;
	ed[tot].next = head[u];
	head[u] = tot++;
}

int qu[N];
int bfs(int u) {
	int l = 0, r = 1;
	int pos = u, val = 0;
	dis[u] = 0;
	qu[0] = u;
	while (l < r) {
		u = qu[l++];
		for (int i = head[u]; ~i; i = ed[i].next) {
			int v = ed[i].v;
			if (dis[v] == -1) {
				pre[v] = u;
				dis[v] = dis[u] + 1;
				qu[r++] = v;
				if (dis[v] > val) {
					val = dis[v];
					pos = v;
				}
			}
		}
	}
	return pos;
}

int flag[N];
bool yes(int ans) {
	memset(flag, 0, sizeof(flag));
	memset(dis, -1, sizeof(dis));
	bfs(route[ans]);
	for (int i = 1; i <= n; i++) {
		if (dis[i] <= ans)
			flag[i] = 1;
	}
	memset(dis, -1, sizeof(dis));
	bfs(route[m - 1 - ans]);
	for (int i = 1; i <= n; i++) {
		if (dis[i] <= ans)
			flag[i] = 1;
	}
	for (int i = 1; i <= n; i++) {
		if (!flag[i])
			return false;
	}
	return true;
}

int main() {
	int i, u, v;
	scanf("%d", &t);
	while (t--) {
		tot = 0;
		memset(head, -1, sizeof(head));
		scanf("%d", &n);
		for (i = 1; i < n; i++) {
			scanf("%d%d", &u, &v);
			add(u, v);
			add(v, u);
		}
		memset(dis, -1, sizeof(dis));
		u = bfs(1);
		memset(dis, -1, sizeof(dis));
		memset(pre, -1, sizeof(pre));
		v = bfs(u);
		m = 0;
		while (v != -1) {
			route[m++] = v;
			v = pre[v];
		}
		int l = 0, r = m - 1, mid, ans[3];
		while (l <= r) {
			mid = (l + r) / 2;
			if (yes(mid)) {
				ans[0] = mid;
				ans[1] = route[mid];
				ans[2] = route[m - 1 - mid];
				if (ans[1] == ans[2]) {
					for (i = 0; i < m; i++) {
						if (route[i] != ans[1]) {
							ans[2] = route[i];
							break;
						}
					}
				}
				r = mid - 1;
			} else
				l = mid + 1;
		}
		printf("%d %d %d\n", ans[0], ans[1], ans[2]);
	}
	return 0;
}


ZOJ 3820 Building Fire Stations