原題傳送門
需要用個點封死一棵樹
答案滿足二分性,直接二分
本題難點在於
貪心思想:駐紮到深度越小的點越優
通過倍增上提軍隊,有些軍隊在二分的mid範圍內走不到根節點,那麼他就駐紮在能走到的最上的地方
有些軍隊可以,那麼他們暫時在根節點休息一會兒,接着從根節點往下走到需要幫助的深度爲2的點(根據貪心思想)
通過一開始走不到根節點的點可以先處理出哪些深度爲2的點其實是不用去管的,它的葉子節點都已被軍隊保護。可以用dfs遍歷處理
接着我用空閒軍隊去幹掉那些遺留的深度爲2的點
採用兩根指針貪心的方法
Code:
#include <bits/stdc++.h>
#define maxn 50010
using namespace std;
struct Edge{
int to, next, len;
}edge[maxn << 1];
struct node{
int u, dis;
}a[maxn], b[maxn];
int num, num1, num2, head[maxn], d[maxn], fa[maxn][25], tag[maxn], len[maxn][25], pos[maxn], n, m;
inline int read(){
int s = 0, w = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') w = -1;
for (; isdigit(c); c = getchar()) s = (s << 1) + (s << 3) + (c ^ 48);
return s * w;
}
void addedge(int x, int y, int z){ edge[++num] = (Edge){y, head[x], z}, head[x] = num; }
bool cmp(node x, node y){ return x.dis < y.dis; }
void dfs(int u, int pre){
d[u] = d[pre] + 1, fa[u][0] = pre;
for (int i = 0; fa[u][i]; ++i) fa[u][i + 1] = fa[fa[u][i]][i], len[u][i + 1] = len[u][i] + len[fa[u][i]][i];
for (int i = head[u]; i; i = edge[i].next){
int v = edge[i].to;
if (v != pre) len[v][0] = edge[i].len, dfs(v, u);
}
}
void cover(int u, int pre){
int p = 1, q = 0;
for (int i = head[u]; i; i = edge[i].next){
int v = edge[i].to;
if (v != pre) cover(v, u), p &= tag[v], q = 1;
}
tag[u] |= ((u != 1) & p & q);
}
bool check(int mid){
memset(tag, 0, sizeof(tag));
num1 = num2 = 0;
for (int i = 1; i <= m; ++i){
int u = pos[i], sum = mid;
for (int j = 20; j >= 0; --j)
if (fa[u][j] && sum >= len[u][j]) sum -= len[u][j], u = fa[u][j];
if (u != 1) tag[u] = 1; else{
u = pos[i];
for (int j = 20; j >= 0; --j) if (fa[u][j] > 1) u = fa[u][j];
a[++num1] = (node){u, sum};
}
}
cover(1, 0);
for (int i = head[1]; i; i = edge[i].next){
int v = edge[i].to;
if (!tag[v]) b[++num2] = (node){v, edge[i].len};
}
sort(a + 1, a + 1 + num1, cmp);
sort(b + 1, b + 1 + num2, cmp);
int j = 1;
for (int i = 1; i <= num1; ++i){
if (!tag[a[i].u]) tag[a[i].u] = 1; else
if (a[i].dis >= b[j].dis) tag[b[j].u] = 1;
while (tag[b[j].u]) ++j;
}
return j > num2;
}
int main(){
n = read();
int l = 0, r = 0, ans = -1;
for (int i = 1; i < n; ++i){
int x = read(), y = read(), z = read();
addedge(x, y, z), addedge(y, x, z);
r += z;
}
dfs(1, 0);
int cnt2 = 0;
for (int i = 1; i <= n; ++i) cnt2 += d[i] == 2;
m = read();
if (m < cnt2) return puts("-1"), 0;
for (int i = 1; i <= m; ++i) pos[i] = read();
while (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;
}