洛谷-P2680 运输计划
运输计划
倍增 LCA + 二分 + 树上差分
显然二分答案是可行的,因为具有单调性,问题在于 check 函数如何写
考虑当前值 mid 是否可行:我们只需要考虑比 mid 大的所有运输计划,将这些计划经过的边进行标记,如果某个边被标记的次数等于所有大于 mid 的运输计划数量,则说明这些运输计划都会经过这条边,此时,这种边才能使得答案可行。因此我们只用找这些边的最大边,将其置 0 后判断答案是否可行
-
找经过的边:考虑用查找 LCA 的方式找
-
给经过的边标记:考虑树上差分,最后再询问一次即可
这题卡常非常严重,我下面这个代码也是偶尔被卡偶尔不被卡
考虑预处理所有路线的 LCA,然后用 dfs 的方式计算所有路径长(用根到两点和 LCA 的距离,容斥算一下)
听说还有树链剖分的做法,待会看看
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
const int maxn = 3e5 + 7;
#define pii pair<int, int>
vector<pii>gra[maxn];
int dep[maxn], n, m, dif[maxn];
pii edge[maxn], lca_val[maxn];
int val[maxn], fa[maxn][25], dis[maxn];
inline int read()
{
int x = 0;
char ch = getchar();
while(ch > '9' || ch < '0') ch = getchar();
while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + ch - '0'; ch = getchar();}
return x;
}
void dfs(int now, int pre, int d)
{
dep[now] = d;
fa[now][0] = pre;
dis[now] = dis[pre] + val[now];
for(auto [nex, x] : gra[now])
{
if(nex == pre) continue;
val[nex] = x;
dfs(nex, now, d + 1);
}
}
int LCA(int a, int b)
{
if(dep[a] < dep[b]) swap(a, b);
int dif = dep[a] - dep[b];
for(int i=19; i>=0; i--)
{
if(dif >= (1 << i))
{
dif -= 1 << i;
a = fa[a][i];
}
}
if(a == b) return a;
for(int i=19; i>=0; i--)
{
if(fa[a][i] != fa[b][i])
{
a = fa[a][i];
b = fa[b][i];
}
}
return fa[a][0];
}
void init(int rt)
{
dfs(rt, rt, 1);
for(int i=1; i<=20; i++)
{
for(int j=0; j<=n; j++)
{
fa[j][i] = fa[fa[j][i-1]][i-1];
}
}
for(int i=0; i<m; i++)
{
int p = LCA(edge[i].first, edge[i].second);
lca_val[i] = {p, dis[edge[i].first] - dis[p] - dis[p] + dis[edge[i].second]};
}
}
void get_ans(int now, int pre)
{
for(auto [nex, x] : gra[now])
{
if(nex == pre) continue;
get_ans(nex, now);
dif[now] += dif[nex];
}
}
int check(int x)
{
for(int i=0; i<=n; i++) dif[i] = 0;
int cnt = 0;
for(int i=0; i<m; i++)
{
if(lca_val[i].second <= x) continue;
dif[lca_val[i].first] -= 2;
dif[edge[i].first]++;
dif[edge[i].second]++;
cnt++;
}
get_ans(1, 1);
int maxx = -1;
for(int i=2; i<=n; i++)
if(dif[i] == cnt) maxx = val[i] > maxx ? val[i] : maxx;
return maxx;
}
int solve()
{
int l = 0, r = 0;
for(int i=0; i<m; i++)
r = max(r, lca_val[i].second);
int maxx = r;
while(l < r)
{
int mid = l + r >> 1;
if(maxx - check(mid) <= mid) r = mid;
else l = mid + 1;
}
return r;
}
int main()
{
n = read();
m = read();
for(int i=1; i<n; i++)
{
int x = read(), y = read(), z = read();
gra[x].push_back({y, z});
gra[y].push_back({x, z});
}
for(int i=0; i<m; i++)
{
int x = read(), y = read();
edge[i] = {x, y};
}
init(1);
printf("%d\n", solve());
return 0;
}