【LOJ2262】「CTSC2017」网络

【题目链接】

【思路要点】

  • 首先,本题一点重要的观察是,新建的路径的两个端点必定在树的直径上,若一个方案新建路径的两个端点有一个不在直径上,我们令其向直径靠近,不会使答案变劣。
  • 因此,我们可以将直径拿出来考虑,令直径上点数为 t o t tot tot ,每一个点为 p o s i pos_i posi p o s i pos_i posi p o s i − 1 pos_{i-1} posi1 在树上的连边长度为 l e n i len_i leni p r e i = ∑ j = 1 i l e n j pre_i=\sum_{j=1}^{i}len_j prei=j=1ilenj
  • 如果我们切断直径上所有的边,树会分成 t o t tot tot 块,每一块的直径是无可避免的,令 L L L 为每一块直径的最大值,则答案至少为 L L L ,至多为直径长度 R R R 。令 d o w n i down_i downi 为从 p o s i pos_i posi 出发在第 i i i 块内的最长路径的长度。
  • 有了答案的区间 [ L , R ] [L,R] [L,R] ,考虑二分答案 m i d mid mid ,判断是否存在使得所有距离不超过 m i d mid mid 的连边方案。
  • 假设我们最终的连边为 a → b   ( a &lt; b ) a\rightarrow b\ (a&lt;b) ab (a<b) ,那么对于任意一对 i &lt; j i&lt;j i<j ,以下两点至少有一点成立是存在方案的充要条件:
    1 1 1 d o w n i + d o w n j + p r e j − p r e i ≤ m i d down_i+down_j+pre_j-pre_i≤mid downi+downj+prejpreimid
    2 2 2 d o w n i + d o w n j + ∣ p r e i − p r e a ∣ + ∣ p r e j − p r e b ∣ + L e n ≤ m i d down_i+down_j+|pre_i-pre_a|+|pre_j-pre_b|+Len≤mid downi+downj+preiprea+prejpreb+Lenmid ,其中 L e n Len Len 为添加的边长
  • 由于要求是 ≤ m i d ≤mid mid ,我们可以枚举 2 2 2 式中的两个绝对值的展开方式,将其转化为 4 4 4 个不带绝对值的限制。
  • p o s i pos_i posi 分别按照 d o w n i + p r e i , d o w n i − p r e i down_i+pre_i,down_i-pre_i downi+prei,downiprei 排序,在第一个顺序中枚举 j j j ,对应满足 1 1 1 式的 i i i 应当是第二个顺序的一个前缀,忽略掉满足 1 1 1 式的 ( i , j ) (i,j) (i,j) ,对于剩余的一定要满足 2 2 2 式的 ( i , j ) (i,j) (i,j) ,我们分别求出出现过最紧的限制 l i m i t 1 , l i m i t 2 , l i m i t 3 , l i m i t 4 limit_1,limit_2,limit_3,limit_4 limit1,limit2,limit3,limit4
  • 接下来问题转化为了判断是否存在 ( a , b ) (a,b) (a,b) ,满足
    1 1 1 − p r e a − p r e b ≤ l i m i t 1 -pre_a-pre_b≤limit_1 preapreblimit1
    2 2 2 − p r e a + p r e b ≤ l i m i t 2 -pre_a+pre_b≤limit_2 prea+preblimit2
    3 3 3 p r e a − p r e b ≤ l i m i t 3 pre_a-pre_b≤limit_3 preapreblimit3
    4 4 4 p r e a + p r e b ≤ l i m i t 4 pre_a+pre_b≤limit_4 prea+preblimit4
  • 从小到大枚举 a a a ,满足以上四个限制的 b b b 均为一个前缀或是后缀,且其端点具有单调性,可用 T w o P o i n t e r s TwoPointers TwoPointers 维护,判断时判断合法的 b b b 的位置是否为空即可。
  • 时间复杂度 O ( N L o g N + N L o g V ) 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值