【BZOJ3242】【UOJ126】【NOI2013】快餐店

【题目链接】

【思路要点】

  • DFS找到环,简单DP一遍,得出每个点的子树的深度\(Depth_i\)。
  • 首先考虑在环上建快餐店。
  • 将环上的每个点投射到其在环上与其相对的位置,建立一个虚点。
  • 将环上点和虚点按照位置排序,并倍长。
  • 枚举快餐店建立在哪两个点/虚点之间,那么这两个点对应的虚点/点逆时针方向的点到快餐店的最远距离应当为\(max\{Depth_i+pos_{restaurant}-pos_i\}\),顺时针方向的点到快餐店的最远距离应当为\(max\{Depth_i-pos_{restaurant}+pos_i\}\)。
  • 换言之,我们需要统计每个半环上\(max\{Depth_i-pos_i\}\)和\(max\{Depth_i+pos_i\}\)的值,显然可以通过单调队列来实现。
  • 然后我们来考虑在树边建快餐店。
  • 那么我们用上面类似的做法对于每个环上的点,统计不在其子树内的距离其最远的点的距离,然后在子树内进行一个基础的树形DP即可。
  • 时间复杂度\(O(NLogN)\),其中除了排序部分,时间复杂度为\(O(N)\)。
  • 不难发现答案只有可能是\(X.0\)或\(X.5\),因此我们读入时可以将所有边的边权翻倍,使用整数计算。
  • 笔者的代码在BZOJ上返回WrongAnswer,但在UOJ以及本地均测试通过。

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 200005;
const int MAXM = 400005;
const long long INF = 1e16;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
struct edge {int dest, len; };
struct info {int p; long long sum, val; };
vector <edge> a[MAXN];
int top, Stack[MAXN], flen[MAXN];
int n, m, p[MAXN], len[MAXN];
bool instack[MAXN], vis[MAXN];
long long f[MAXN], g[MAXN], h[MAXN], val[MAXN];
long long premax[MAXN], sufmax[MAXN], ans;
info b[MAXM];
bool cmp(info a, info b) {
	return a.sum < b.sum;
}
void dfs(int pos, int fa) {
	Stack[++top] = pos;
	vis[pos] = true;
	instack[pos] = true;
	for (unsigned i = 0; i < a[pos].size(); i++)
		if (!vis[a[pos][i].dest]) {
			flen[a[pos][i].dest] = a[pos][i].len;
			dfs(a[pos][i].dest, pos);
		} else if (a[pos][i].dest == fa) continue;
		else if (instack[a[pos][i].dest]) {
			for (int j = top; Stack[j] != a[pos][i].dest; j--) {
				int tmp = Stack[j]; m++;
				p[m] = tmp, len[m] = flen[tmp];
			}
			p[++m] = a[pos][i].dest;
			len[m] = a[pos][i].len;
		}
	top--;
	instack[pos] = false;
}
void dp(int pos, int fa) {
	f[pos] = 0; g[pos] = 0;
	for (unsigned i = 0; i < a[pos].size(); i++)
		if (a[pos][i].dest != fa && !vis[a[pos][i].dest]) {
			dp(a[pos][i].dest, pos);
			long long tmp = f[a[pos][i].dest] + a[pos][i].len;
			if (tmp > f[pos]) {
				g[pos] = f[pos];
				f[pos] = tmp;
			} else chkmax(g[pos], tmp);
		}
}
void pd(int pos, int fa) {
	for (unsigned i = 0; i < a[pos].size(); i++)
		if (a[pos][i].dest != fa && !vis[a[pos][i].dest]) {
			long long tmp = f[a[pos][i].dest] + a[pos][i].len;
			long long tnp = h[pos];
			if (tmp == f[pos]) chkmax(tnp, g[pos]);
			else chkmax(tnp, f[pos]);
			h[a[pos][i].dest] = a[pos][i].len + tnp;
			tmp = f[a[pos][i].dest];
			long long now;
			if (abs(tmp - tnp) >= a[pos][i].len) now = max(tmp, tnp);
			else now = (tmp + tnp + a[pos][i].len) / 2;
			chkmin(ans, now);
			pd(a[pos][i].dest, pos);
		}
}
int main() {
	read(n);
	for (int i = 1; i <= n; i++) {
		int x, y, z;
		read(x), read(y), read(z); z *= 2;
		a[x].push_back((edge) {y, z});
		a[y].push_back((edge) {x, z});
	}
	/*Find A Loop*/
	dfs(1, 0);
	/*Find A Loop*/
	/*Double This Loop && DP -> f[i], g[i]*/
	memset(vis, false, sizeof(vis));
	for (int i = 1; i <= m; i++)
		vis[p[i]] = true;
	long long sum = 0;
	for (int i = 1; i <= m; i++) {
		dp(p[i], 0);
		val[i] = f[p[i]];
		sum += len[i];
	}
	long long pre = 0;
	int tot = 0;
	for (int i = 1; i <= m; i++) {
		b[++tot] = (info) {p[i], pre, val[i]};
		if (pre + sum / 2 <= sum) b[++tot] = (info) {0, pre + sum / 2, -INF};
		else b[++tot] = (info) {0, pre - sum / 2, -INF};
		pre += len[i];
	}
	sort(b + 1, b + tot + 1, cmp);
	for (int i = 1; i <= tot; i++) {
		b[tot + i] = b[i];
		b[tot + i].sum += sum;
	}
	/*Double This Loop && DP -> f[i], g[i]*/
	/*PartI : On Loop*/
	static int q[MAXM]; int l = 0, r = -1;
	for (int i = 1; i <= tot; i++) {
		while (l <= r && b[i].val - b[i].sum >= b[q[r]].val - b[q[r]].sum) r--;
		q[++r] = i;
	}
	for (int i = tot + 1; i <= tot * 2; i++) {
		while (l <= r && b[i].sum - b[q[l]].sum > sum / 2) l++;
		while (l <= r && b[i].val - b[i].sum >= b[q[r]].val - b[q[r]].sum) r--;
		q[++r] = i;
		if (l <= r) premax[i - tot] = b[q[l]].val + b[i].sum - b[q[l]].sum;
		else premax[i - tot] = -INF;
	}
	l = 0, r = -1;
	for (int i = tot * 2; i >= tot + 1; i--) {
		while (l <= r && b[i].val + b[i].sum >= b[q[r]].val + b[q[r]].sum) r--;
		q[++r] = i;
	}
	for (int i = tot; i >= 1; i--) {
		while (l <= r && b[q[l]].sum - b[i].sum > sum / 2) l++;
		while (l <= r && b[i].val + b[i].sum >= b[q[r]].val + b[q[r]].sum) r--;
		q[++r] = i;
		if (l <= r) sufmax[i] = b[q[l]].val - b[i].sum + b[q[l]].sum;
		else sufmax[i] = -INF;
	}
	premax[1 + tot] = premax[1]; ans = INF;
	for (int i = 1; i <= tot; i++) {
		long long tmp = b[i + 1].sum - b[i].sum;
		long long pre = premax[i + 1];
		long long suf = sufmax[i];
		long long now;
		if (abs(pre - suf) >= tmp) now = max(pre, suf) - tmp;
		else now = (pre + suf - tmp) / 2;
		chkmin(ans, now);
	}
	/*PartI : On Loop*/
	/*PartII : On Tree*/
	l = 0, r = -1;
	for (int i = 1; i <= tot; i++) {
		while (l <= r && b[i].val - b[i].sum >= b[q[r]].val - b[q[r]].sum) r--;
		q[++r] = i;
	}
	for (int i = tot + 1; i <= tot * 2; i++) {
		while (l <= r && b[i].sum - b[q[l]].sum > sum / 2) l++;
		if (l <= r) premax[i - tot] = b[q[l]].val + b[i].sum - b[q[l]].sum;
		else premax[i - tot] = -INF;
		while (l <= r && b[i].val - b[i].sum >= b[q[r]].val - b[q[r]].sum) r--;
		q[++r] = i;
	}
	l = 0, r = -1;
	for (int i = tot * 2; i >= tot + 1; i--) {
		while (l <= r && b[i].val + b[i].sum >= b[q[r]].val + b[q[r]].sum) r--;
		q[++r] = i;
	}
	for (int i = tot; i >= 1; i--) {
		while (l <= r && b[q[l]].sum - b[i].sum > sum / 2) l++;
		if (l <= r) sufmax[i] = b[q[l]].val - b[i].sum + b[q[l]].sum;
		else sufmax[i] = -INF;
		while (l <= r && b[i].val + b[i].sum >= b[q[r]].val + b[q[r]].sum) r--;
		q[++r] = i;
	}
	for (int i = 1; i <= tot; i++) {
		if (b[i].p == 0) continue;
		h[b[i].p] = max(premax[i], sufmax[i]);
		pd(b[i].p, 0);
	}
	/*PartII : On Tree*/
	if (ans % 2 == 0) printf("%lld.0\n", ans / 2);
	else printf("%lld.5\n", ans / 2);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_39972971/article/details/80456043