P4886 快递员
呃呃呃呃呃,兄弟们,终于AC快递员了,呃呃呃,这都什么日子啊,天天吃 18 发罚时。(来,兄弟们先吃)
思路
虽然有一堆点对,但是我们只用关注那些最长的点对即可,因为如果最长的点对已经不能放短了,那么你再怎么调其他的都改变不了现实,不过我们也要枚举每个点,所以时间还是很大,瓶颈在于会有很多不去要的冗余点,考虑直径最长,而直径上最为标志,最为好算的点就是重心,所以我们考虑每次跳重心,然后暴力算,那么一切将会分为一下几种情况:
- 当前最大的存在一条过重心,那么怎么动都会变得更大,直接输出
- 当前最大的边分布在了不同的重心的儿子子树中,那么同理动一次总有链被增加,所以也可以直接输出
- 以上都不满足,往最大边分布的儿子子树找重心然后跳即可
思路很简单,不过细节有点多
#include <iostream>
#include <vector>
using namespace std;
const int MaxN = 2e5 + 10, MaxK = 19;
struct S {
int v, w;
};
int dis[MaxN], sz[MaxN], bsz[MaxN], gt[MaxN], que[MaxN], a[MaxN], b[MaxN], qtg, root, ans = 1e9, tot, n, tn, m;
vector<S> g[MaxN];
bool vis[MaxN];
void find(int x, int fa) {
sz[x] = 1, bsz[x] = 0;
for (auto i : g[x]) {
if (vis[i.v] || i.v == fa) continue;
find(i.v, x);
sz[x] += sz[i.v];
bsz[x] = max(bsz[x], sz[i.v]);
}
bsz[x] = max(bsz[x], n - sz[x]);
if (bsz[x] < bsz[root]) {
root = x;
}
}
void GOGOGO(int x, int rt, int fa = -1) {
gt[x] = rt;
for (auto i : g[x]) {
if (i.v == fa) continue;
dis[i.v] = dis[x] + i.w;
GOGOGO(i.v, rt, x);
}
}
void DFS(int x) {
if (vis[x]) {
cout << ans << endl;
exit(0);
}
vis[x] = 1, dis[x] = 0;
for (auto i : g[x]) {
dis[i.v] = i.w, GOGOGO(i.v, i.v, x);
}
int mxl = 0;
for (int i = 1, tmp; i <= m; i++) {
tmp = dis[a[i]] + dis[b[i]];
if (mxl == tmp) que[++qtg] = i;
if (mxl < tmp) {
mxl = tmp, que[(qtg = 1)] = i;
}
}
ans = min(ans, mxl);
int lst = 0;
for (int j = 1, tmp; j <= qtg; j++) {
int i = que[j];
if (gt[a[i]] != gt[b[i]]) {
cout << ans << endl;
exit(0);
}
if (!lst) lst = gt[a[i]];
if (lst != gt[a[i]]) {
cout << ans << endl;
exit(0);
}
}
root = 0, n = sz[lst], find(lst, 0), DFS(root);
}
signed main() {
ios::sync_with_stdio(0), cin.tie(0);
cin >> n >> m, tn = n;
for (int i = 1, u, v, w; i < n; i++) {
cin >> u >> v >> w;
g[u].push_back({v, w});
g[v].push_back({u, w});
}
for (int i = 1; i <= m; i++) {
cin >> a[i] >> b[i];
}
root = 0, bsz[0] = 1e9, find(1, 0), DFS(root);
cout << ans << endl;
return 0;
}