【LOJ2262】「CTSC2017」网络

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_39972971/article/details/83661841

【题目链接】

【思路要点】

  • 首先,本题一点重要的观察是,新建的路径的两个端点必定在树的直径上,若一个方案新建路径的两个端点有一个不在直径上,我们令其向直径靠近,不会使答案变劣。
  • 因此,我们可以将直径拿出来考虑,令直径上点数为 tottot ,每一个点为 posipos_iposipos_iposi1pos_{i-1} 在树上的连边长度为 lenilen_iprei=j=1ilenjpre_i=\sum_{j=1}^{i}len_j
  • 如果我们切断直径上所有的边,树会分成 tottot 块,每一块的直径是无可避免的,令 LL 为每一块直径的最大值,则答案至少为 LL ,至多为直径长度 RR 。令 downidown_i 为从 posipos_i 出发在第 ii 块内的最长路径的长度。
  • 有了答案的区间 [L,R][L,R] ,考虑二分答案 midmid ,判断是否存在使得所有距离不超过 midmid 的连边方案。
  • 假设我们最终的连边为 ab (a<b)a\rightarrow b\ (a<b) ,那么对于任意一对 i<ji<j ,以下两点至少有一点成立是存在方案的充要条件:
    11downi+downj+prejpreimiddown_i+down_j+pre_j-pre_i≤mid
    22downi+downj+preiprea+prejpreb+Lenmiddown_i+down_j+|pre_i-pre_a|+|pre_j-pre_b|+Len≤mid ,其中 LenLen 为添加的边长
  • 由于要求是 mid≤mid ,我们可以枚举 22 式中的两个绝对值的展开方式,将其转化为 44 个不带绝对值的限制。
  • posipos_i 分别按照 downi+prei,downipreidown_i+pre_i,down_i-pre_i 排序,在第一个顺序中枚举 jj ,对应满足 11 式的 ii 应当是第二个顺序的一个前缀,忽略掉满足 11 式的 (i,j)(i,j) ,对于剩余的一定要满足 22 式的 (i,j)(i,j) ,我们分别求出出现过最紧的限制 limit1,limit2,limit3,limit4limit_1,limit_2,limit_3,limit_4
  • 接下来问题转化为了判断是否存在 (a,b)(a,b) ,满足
    11preapreblimit1-pre_a-pre_b≤limit_1
    22prea+preblimit2-pre_a+pre_b≤limit_2
    33preapreblimit3pre_a-pre_b≤limit_3
    44prea+preblimit4pre_a+pre_b≤limit_4
  • 从小到大枚举 aa ,满足以上四个限制的 bb 均为一个前缀或是后缀,且其端点具有单调性,可用 TwoPointersTwoPointers 维护,判断时判断合法的 bb 的位置是否为空即可。
  • 时间复杂度 O(NLogN+NLogV)O(NLogN+NLogV)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 5;
const long long INF = 1e18;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
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("");
}
int n; ll len, l, r; bool vis[MAXN];
vector <pair <int, int> > a[MAXN];
int dfsans, father[MAXN], fedge[MAXN]; ll sum[MAXN];
int tot, pos[MAXN], pa[MAXN], pb[MAXN]; ll down[MAXN], pre[MAXN];
ll limit1, limit2, limit3, limit4;
//pa : down + pre, pb : down - pre;
//1 : --, 2 : -+, 3 : +-, 4 : ++.
bool check(ll mid) {
	limit1 = limit2 = limit3 = limit4 = INF;
	ll Min1 = INF, Min2 = INF, Min3 = INF, Min4 = INF;
	for (int j = 1, i = tot; j <= tot; j++) {
		while (i != 0 && down[pb[i]] - pre[pb[i]] + down[pa[j]] + pre[pa[j]] > mid) {
			int tmp = pb[i--];
			chkmin(Min1, -down[tmp] - pre[tmp]);
			chkmin(Min2, -down[tmp] - pre[tmp]);
			chkmin(Min3, -down[tmp] + pre[tmp]);
			chkmin(Min4, -down[tmp] + pre[tmp]);
		}
		int tmp = pa[j];
		chkmin(limit1, -down[tmp] - pre[tmp] + Min1);
		chkmin(limit2, -down[tmp] + pre[tmp] + Min2);
		chkmin(limit3, -down[tmp] - pre[tmp] + Min3);
		chkmin(limit4, -down[tmp] + pre[tmp] + Min4);
	}
	limit1 += mid - len, limit2 += mid - len;
	limit3 += mid - len, limit4 += mid - len;
	int pos1 = tot + 1, pos2 = 0, pos3 = 1, pos4 = tot;
	for (int i = 1; i <= tot; i++) {
		while (pos1 - 1 >= 1 && -pre[i] - pre[pos1 - 1] <= limit1) pos1--;
		while (pos2 + 1 <= tot && -pre[i] + pre[pos2 + 1] <= limit2) pos2++;
		while (pos3 <= tot && pre[i] - pre[pos3] > limit3) pos3++;
		while (pos4 >= 1 && pre[i] + pre[pos4] > limit4) pos4--;
		int l = max(pos1, pos3), r = min(pos2, pos4);
		if (l <= r) return true;
	}
	return false;
}
void dfs(int pos, int fa) {
	if (sum[pos] > sum[dfsans]) dfsans = pos;
	for (auto x : a[pos])
		if (x.first != fa && !vis[x.first]) {
			sum[x.first] = sum[pos] + x.second;
			fedge[x.first] = x.second;
			father[x.first] = pos;
			dfs(x.first, pos);
		}
}
int dfs(int from) {
	dfsans = from;
	sum[from] = father[from] = 0;
	dfs(from, 0);
	return dfsans;
}
int main() {
	for (scanf("%d%lld", &n, &len); n != 0; scanf("%d%lld", &n, &len)) {
		for (int i = 1; i <= n; i++)
			a[i].clear();
		for (int i = 1; i <= n - 1; i++) {
			int x, y, z;
			read(x), read(y), read(z);
			a[x].emplace_back(y, z);
			a[y].emplace_back(x, z);
		}
		memset(vis, false, sizeof(vis)), tot = 0;
		for (int i = dfs(dfs(1)); i != 0; i = father[i]) {
			pos[++tot] = i;
			vis[i] = true;
			pre[tot + 1] = fedge[i];
		}
		l = 0;
		for (int i = 1; i <= tot; i++) {
			pre[i] += pre[i - 1];
			pa[i] = pb[i] = i;
			int now = pos[i];
			vis[now] = false;
			int tmp = dfs(now);
			down[i] = sum[tmp];
			chkmax(l, sum[dfs(tmp)]);
			vis[now] = true;
		}
		sort(pa + 1, pa + tot + 1, [&] (int x, int y) {return down[x] + pre[x] < down[y] + pre[y]; });
		sort(pb + 1, pb + tot + 1, [&] (int x, int y) {return down[x] - pre[x] < down[y] - pre[y]; });
		r = pre[tot];
		while (l < r) {
			ll mid = (l + r) / 2;
			if (check(mid)) r = mid;
			else l = mid + 1;
		}
		printf("%lld\n", l);
	}
	return 0;
}
阅读更多
换一批

没有更多推荐了,返回首页