codevs4632: [NOIP2015]运输计划

题目链接
半年前写过,然而是照着别人的模板敲的,今天自己写一遍。
dfs序+LCA+二分+树上差分。
然而我又写了树链剖分,简单粗暴。

#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

const int N = 300030;

struct edge{
	int v, t;
};
vector<edge> E[N];

int n, m;
int size[N], son[N], dep[N], fa[N], top[N], rank[N], idx, dis[N];

void dfs1(int u, int f) {
	size[u] = 1; dep[u] = dep[fa[u] = f] + 1;
	for (int i = 0; i < E[u].size(); i++) {
		int &v = E[u][i].v, &t = E[u][i].t;
		if (v == f) continue;
		dis[v] = dis[u] + t;
		dfs1(v, u);
		size[u] += size[v];
		if (size[son[u]] < size[v])
			son[u] = v;
	}
}

void dfs2(int u, int tp) {
	top[u] = tp; rank[++idx] = u;
	if (! son[u]) return;
	dfs2(son[u], tp);
	for (int i = 0; i < E[u].size(); i++) {
		int &v = E[u][i].v;
		if (v == fa[u] or v == son[u]) continue;
		dfs2(v, v);
	}
}

inline int getLca(int u, int v) {
	int f1 = top[u], f2 = top[v];
	while (f1 != f2) {
		if (dep[f1] < dep[f2]) {
			swap(u, v); swap(f1, f2);
		}
		u = fa[f1]; f1 = top[u];
	}
	return dep[u] < dep[v] ? u : v;
}

int g_u[N], g_v[N], cost[N], lca[N], cap;
inline void init() {
	cin >> n >> m;
	for (int i = 1; i < n; i++) {
		int a, b, t;
		scanf("%d%d%d", &a, &b, &t);
		E[a].push_back((edge){b, t});
		E[b].push_back((edge){a, t});
	}
	dfs1(1, 0);
	dfs2(1, 1);
	for (int i = 1; i <= m; i++) {
		scanf("%d%d", &g_u[i], &g_v[i]);
		cost[i] = dis[g_u[i]] + dis[g_v[i]] - dis[lca[i] = getLca(g_u[i], g_v[i])] * 2;
		cap = max(cap, cost[i]);
	}
}

int cover[N];
inline bool judge(int ans) {
	memset(cover, 0, sizeof(cover));
	int cnt = 0, ret = 0;
	for (int i = 1; i <= m; i++) if (cost[i] > ans) {
		cover[g_u[i]]++; cover[g_v[i]]++; cover[lca[i]] -= 2, cnt++;
		ret = max(ret, cost[i] - ans);
	}
	for (int i = n; i > 1; i--)
		cover[fa[rank[i]]] += cover[rank[i]];
	for (int i = 1; i <= n; i++)
		if (cover[i] == cnt and dis[i] - dis[fa[i]] >= ret)
			return true;
	return false;
}

inline void work() {
	int l = 0, r = cap;
	while (l < r) {
		int mid = l + r >> 1;
		if (judge(mid)) r = mid;
		else l = mid + 1;
	}
	cout << l << endl;
}

int main() {
	init();
	work();
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值