如果在较短的时间内能控制,那么在较长时间内也一定可以,这意味着我们可以二分答案
二分时间,就是军队走过的距离
容易发现一支军队它的深度约浅它所封锁的叶子会单调不减
所以我们贪心地把这些军队往高处跳,第一层的节点(根的所有儿子节点)一定是一个相较当前子树其他位置更优的点
在跳完了之后,可能会有一些情况
当一个军队跳不到根节点,我们就把它放在那里,这个位置一定是对于这个军队来说最优的位置
根节点是不可以有军队的,所以我们还需要对跳的距离超过出发点距根节点距离的军队进行讨论
对于刚好跳到根节点的军队,我们强制让它少走一步
对于跳的距离超过了根的军队,我们把丢进一个数据结构里,之后再决定它们去哪
dfs 一遍,求出每个节点到它子树的叶节点路径是否都被封锁,打上是否被封锁的标记
将第一层的 有标记的的节点 也丢进数据结构里,并按距根的距离排序,也把之前存军队的数据结构按它们走到根之后还可以走多少排序
扫描一遍所有在数据结构中的军队,如果它的除根节点外最浅的祖先没有标记,就把它放到该节点,否则判断它能走的距离是否比当前存节点的数据结构中最靠前的节点距根距离大,成立则把它放到这个节点,否则这支军队没用
注意刚才这一套操作的顺序,它是不能变的,因为有可能一些较浅的军队可以去更新其他子树中的点,而且这个点别的军队无法在时限内到达,而这个军队当前所在的子树是能被其他军队更新的,如果两种操作分开两次 O(n) 扫描,会漏掉上面的情况,答案就会更劣
扫描完后如果根也符合打标记的要求,则这个答案成立
如此二分下去即可
代码:
#include<algorithm> #include<iostream> #include<cstdlib> #include<cstring> #include<cctype> #include<cstdio> #include<queue> #include<cmath> using namespace std; const int MAXN = 50005; struct EDGE{ int nxt, to, val; EDGE(int NXT = 0, int TO = 0, int VAL = 0) {nxt = NXT; to = TO; val = VAL;} }edge[MAXN << 1]; int n, m, totedge, mxchn = 1, top1, top2, lg; int head[MAXN], dst[MAXN], pos[MAXN], d[MAXN]; int top[MAXN], f[MAXN][21], deg[MAXN]; pair<int,int> stk1[MAXN], stk2[MAXN]; bool tag[MAXN], has[MAXN]; queue<int> q; inline void add(int x, int y, int v) { edge[++totedge] = EDGE(head[x], y, v); head[x] = totedge; return; } inline void bfs() { dst[1] = 0; q.push(1); while(!q.empty()) { int x = q.front(); q.pop(); if(dst[x] > dst[mxchn]) mxchn = x; if(f[x][0] == 1) top[x] = x; for(int i = head[x]; i; i = edge[i].nxt) if(!dst[edge[i].to] && edge[i].to != f[x][0]) { int y = edge[i].to; dst[y] = dst[x] + edge[i].val; top[y] = top[x]; f[y][0] = x; q.push(y); for(int i = 1; i <= lg; ++i) f[y][i] = f[f[y][i - 1]][i - 1]; } } return; } inline void cfs(int bgn) { mxchn = 0; for(int i = 1; i <= n; ++i) d[i] = 0x3f3f3f3f; d[bgn] = 0; q.push(bgn); while(!q.empty()) { int x = q.front(); q.pop(); if(d[x] > d[mxchn]) mxchn = x; for(int i = head[x]; i; i = edge[i].nxt) if(d[edge[i].to] == 0x3f3f3f3f) { d[edge[i].to] = d[x] + edge[i].val; q.push(edge[i].to); } } return; } inline int jump(int x, int len) { if(len > dst[x]) return -1; if(len == dst[x]) --len; register int cur = x; for(int i = lg; i >= 0 && len; --i) if((dst[cur] - dst[f[cur][i]] <= len) && f[cur][i]) { len -= (dst[cur] - dst[f[cur][i]]); cur = f[cur][i]; } return cur; } bool pushup(int x) { if(tag[x]) return true; if(deg[x] == 1 && x != 1) return has[x] = false; has[x] = true; for(int i = head[x]; i; i = edge[i].nxt) if(edge[i].to != f[x][0]) { int y = edge[i].to; if(!pushup(y)) has[x] = false; } return has[x]; } inline bool chk(int mid) { top1 = 0; top2 = 0; for(int i = 1; i <= n; ++i) tag[i] = has[i] = false; for(int i = 1; i <= m; ++i) { int y = jump(pos[i], mid); if(y == -1) stk1[++top1] = make_pair(mid - dst[pos[i]], pos[i]); else tag[y] = has[y] = true; } pushup(1); for(int i = head[1]; i; i = edge[i].nxt) { int y = edge[i].to; if(!has[y]) { stk2[++top2] = make_pair(dst[y], y); } } sort(stk1 + 1, stk1 + top1 + 1); sort(stk2 + 1, stk2 + top2 + 1); int ptr = 1; for(int i = 1; i <= top1 && ptr <= top2; ++i) { if(!has[top[stk1[i].second]]) { has[top[stk1[i].second]] = true; continue; } while(ptr <= top2 && has[stk2[ptr].second]) ++ptr; if(stk2[ptr].first <= stk1[i].first) { has[stk2[ptr].second] = true; ++ptr; } } for(int i = head[1]; i; i = edge[i].nxt) if(!has[edge[i].to]) return false; return true; } inline void hfs(int l, int r) { int mid; while(l < r) { mid = ((l + r) >> 1); if(chk(mid)) r = mid; else l = mid + 1; } printf("%d ", l); return; } int main() { scanf("%d", &n); lg = (int)ceil(log(n) / log(2)) + 1; register int xx, yy, vv; for(int i = 1; i < n; ++i) { scanf("%d%d%d", &xx, &yy, &vv); add(xx, yy, vv); add(yy, xx, vv); ++deg[xx]; ++deg[yy]; } scanf("%d", &m); for(int i = 1; i <= m; ++i) scanf("%d", &pos[i]); int tmp = m; for(int i = head[1]; i; i = edge[i].nxt) --tmp; if(tmp < 0) { puts("-1"); return 0; } bfs(); cfs(mxchn); hfs(0, d[mxchn]); return 0; }