「NOIP2015」运输计划
知识点:二分答案,树上差分
题意简述
给定一棵 \(n\) 个节点的树,边有边权。
给定 \(m\) 条路径,定义花费为所有路径长度的最大值。
现可将任意一条边的权值置零,求最小的花费。
\(100\le n\le 3\times 10^5\),\(1\le m\le 3\times 10^5\),\(0\le\) 边权 \(\le 10^3\)
分析题意
最大值最小,考虑二分答案,枚举置零边权后路径长度的最大值 \(mid\)。
Check
时仅需考虑长度 \(\ge mid\) 的路径即可,其他路径已经合法。
问题转化为能否找到一条边,同时在所有长度 \(\ge mid\) 的路径上,且将其置零后能使所有路径合法。
即其权值 \(\ge\) 最长的路径长度 \(-mid\)。
对于 同时出现 这一限制,考虑树上差分维护各边在路径中 出现的次数。
对于 权值大小 这一限制,预处理出所有路径的长度。
Dfs 还原时判断是否存在一条边满足上述条件即可。
修改的路径是已知的,可先预处理所有的 lca,差分时直接调用。
单次 Check
复杂度上限为 \(O(m + n)\),\(n,m\) 同阶,总复杂度 \(O(n\log n)\) 级别。
爆零小技巧
轻微卡常,注意实现。
代码实现
//知识点:二分答案,树上差分
/*
By:Luckyblock
*/
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstring>
#define ll long long
const int kMaxn = 3e5 + 10;
const int kMaxm = (kMaxn << 1);
//=============================================================
struct Route {
int a, b, lca, val;
} a[kMaxn];
int n, m, ans;
int e_num, head[kMaxn], v[kMaxn << 1], w[kMaxn << 1], ne[kMaxn << 1];
int dep[kMaxn], fa[kMaxn][25], sum[kMaxn][25];
int diff[kMaxn];
//=============================================================
inline int read() {
int f = 1, w = 0;
char ch = getchar();
for (; !isdigit(ch); ch = getchar())
if (ch == '-') f = -1;
for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
return f * w;
}
void Chkmax(int &fir_, int sec_) {
if (sec_ > fir_) fir_ = sec_;
}
void Chkmin(int &fir_, int sec_) {
if (sec_ < fir_) fir_ = sec_;
}
void AddEdge(int u_, int v_, int w_) {
v[++ e_num] = v_, w[e_num] = w_;
ne[e_num] = head[u_], head[u_] = e_num;
}
bool CompareRoute(Route fir, Route sec) {
return fir.val > sec.val;
}
void Dfs1(int u_, int fa_) {
dep[u_] = dep[fa_] + 1;
fa[u_][0] = fa_;
for (int i = 1; i <= 22; ++ i) {
fa[u_][i] = fa[fa[u_][i - 1]][i - 1];
sum[u_][i] = sum[u_][i - 1] + sum[fa[u_][i - 1]][i - 1];
}
for (int i = head[u_]; i; i = ne[i]) {
int v_ = v[i], w_ = w[i];
if (v_ == fa_) continue ;
sum[v_][0] = w_;
Dfs1(v_, u_);
}
}
void GetLca(int id_, int u_, int v_) {
if (dep[v_] > dep[u_]) std :: swap(u_, v_);
for (int i = 22; i >= 0; -- i) {
if (dep[fa[u_][i]] >= dep[v_]) {
a[id_].val += sum[u_][i];
u_ = fa[u_][i];
}
}
if (u_ == v_) {
a[id_].lca = u_;
return ;
}
for (int i = 22; i >= 0; -- i) {
if (fa[u_][i] != fa[v_][i]) {
a[id_].val += sum[u_][i] + sum[v_][i];
u_ = fa[u_][i];
v_ = fa[v_][i];
}
}
a[id_].val += sum[u_][0] + sum[v_][0];
a[id_].lca = fa[u_][0];
}
bool Dfs2(int u_, int fa_, int cnt_, int lim_) {
for (int i = head[u_]; i; i = ne[i]) {
int v_ = v[i];
if (v_ == fa_) continue ;
if (Dfs2(v_, u_, cnt_, lim_)) return true;
diff[u_] += diff[v_];
}
return (diff[u_] == cnt_ && a[1].val - sum[u_][0] <= lim_);
}
bool Check(int lim_) {
memset(diff, 0, sizeof (diff));
int cnt = 0;
for (int i = 1; i <= m; ++ i) {
if (a[i].val <= lim_) break;
++ cnt;
diff[a[i].a] ++;
diff[a[i].b] ++;
diff[a[i].lca] -= 2;
}
return Dfs2(1, 0, cnt, lim_);
}
//=============================================================
int main() {
n = read(), m = read();
for (int i = 1; i < n; ++ i) {
int u_ = read(), v_ = read(), w_ = read();
AddEdge(u_, v_, w_), AddEdge(v_, u_, w_);
}
Dfs1(1, 0);
for (int i = 1; i <= m; ++ i) {
a[i].a = read(), a[i].b = read();
GetLca(i, a[i].a, a[i].b);
}
std :: sort(a + 1, a + m + 1, CompareRoute);
for (int l = 1, r = a[1].val; l <= r; ) {
int mid = (l + r) >> 1;
if (Check(mid)) {
ans = mid;
r = mid - 1;
} else {
l = mid + 1;
}
}
printf("%d\n", ans);
return 0;
}
作者@Luckyblock,转载请声明出处。