答案具有单调性:如果 t 时刻能完成检查点建立,比 t 大的时刻一定行,因此答案具有单调性(这题正是要求所有军队的移动时间的最大值,即最大值最小化问题)
如果需要花时间来移动军队,最优的做法是所有军队都往上移。既然如此,可以二分时间,在这个时间内尽可能的把所有军队往上移,但不要移到根节点。
考虑如何check:若军队不能移动到根结点,那这些军队肯定无法再移动,直接就地建立检查点,此时用dfs搜一下根节点的哪些子节点需要驻军(若存在叶子没有被覆盖,那么这个信息一定会被返回到根节点的子节点,在这个点驻军是最优的选择)。
对于可以移动到根节点的军队,拿这些军队去驻军 上一步dfs后得到的需要驻军的点。存下它们的剩余时间,贪心的策略每一个需要驻军的点找一支所剩时间尽可能短的军队去驻军。
对这些军队按剩余时间进行排序。
若一支军队是从 p 结点移动到根节点(p是根节点的一个子节点),却无法从根节点返回p结点,且 p 需要驻军,那么这支军队直接在 p 结点驻军,无需移动到根节点。
这样做是最优的,因为这支结点驻守了他去根节点就无法驻守的结点(试想一下所有结点都到了 根节点,一个军队去驻守一个需要时间 > 它的所剩时间的点,显然发挥了这个军队的全部价值,而这种价值只能在这种情况下发挥)
剩余的点前面的贪心策略匹配即可。
有点卡常,需要用邻接表来存图
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 5e4 + 10;
typedef long long ll;
#define pii pair<int,int>
#define pli pair<ll,int>
pli tmp[maxn];
ll vp[maxn],tp[maxn];
int svp,stp,stmp;
int head[maxn * 5],to[maxn * 5],nxt[maxn * 5],c[maxn * 5],cnt = 0;
void add(int u,int v,int w) {
to[cnt] = v;
c[cnt] = w;
nxt[cnt] = head[u];
head[u] = cnt++;
}
int vis[maxn],p[maxn][21];
ll w[maxn][21];
int n,m;
int pt[maxn];
void pre(int u,int fa) {
for(int i = 1; i <= 17; i++) {
p[u][i] = p[p[u][i - 1]][i - 1];
w[u][i] = w[p[u][i - 1]][i - 1] + w[u][i - 1];
}
for(int i = head[u]; i + 1; i = nxt[i]) {
if(to[i] == fa) continue;
p[to[i]][0] = u;
w[to[i]][0] = c[i];
pre(to[i],u);
}
}
int dfs(int u,int fa) {
if(vis[u]) return 1;
int cnt = 0;
for(int i = head[u]; i + 1; i = nxt[i]) {
if(to[i] == fa) continue;
cnt++;
if(!dfs(to[i],u)) return 0;
}
return cnt;
}
void clib(ll lim) {
for(int i = 1; i <= m; i++) {
int v = pt[i];
ll sum = 0;
for(int i = 17; i >= 0; i--) {
if(p[v][i] > 1 && sum + w[v][i] <= lim) {
sum += w[v][i];
v = p[v][i];
}
}
if(p[v][0] == 1 && sum + w[v][0] <= lim) {
tmp[++stmp] = pli(lim - sum - w[v][0],v);
} else {
vis[v] = 1;
}
}
}
bool check(ll lim) {
memset(vis,0,sizeof vis);
stmp = stp = svp = 0;
clib(lim);
for(int i = head[1]; i + 1; i = nxt[i])
vis[to[i]] = dfs(to[i],1);
sort(tmp + 1,tmp + stmp + 1);
for(int i = 1; i <= stmp; i++) {
pli it = tmp[i];
if(!vis[it.second] && it.first < w[it.second][0]) {
vis[it.second] = 1;
} else {
vp[++svp] = it.first;
}
}
for(int i = head[1]; i + 1; i = nxt[i]) { //需要驻守的点
if(!vis[to[i]])
tp[++stp] = 1ll * c[i];
}
sort(tp + 1,tp + stp + 1);
int i = 1,j = 1;
while(i <= stp && j <= svp) {
if(vp[j] >= tp[i]) {
i++,j++;
} else {
j++;
}
}
return i > stp;
}
ll solve() {
ll l = 0, r = 5e14;
while(l < r) {
ll mid = l + r >> 1;
if(check(mid)) r = mid;
else l = mid + 1;
}
return l;
}
int main() {
scanf("%d",&n);
memset(head,-1,sizeof head);
for(int i = 1,u,v,w; i < n; i++) {
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
add(v,u,w);
}
scanf("%d",&m);
for(int i = 1; i <= m; i++) {
scanf("%d",&pt[i]);
}
pre(1,0);
ll ans = solve();
ans = ans == 5e14 ? -1 : ans;
printf("%lld\n",ans);
return 0;
}