[题目链接]
https://www.luogu.org/problemnew/show/P1084
[算法]
细心观察发现 : 此题的答案具有单调性,也就是说,如果p小时能控制疫情,那么q小时也能控制疫情(q > p),因此我们可以二分答案,这是此题的突破口
问题就转化为了检验”Mid小时是否可以控制住疫情“
我们发现,既然要求所有叶子节点得到管辖,那么,军队所在的节点深度越浅,所能管辖的节点数就越多,我们不妨让每支军队都先移动到所能移动的最顶端(不能移动到根节点),具体实现时,我们可以通过倍增预处理每个节点向上2^j条边的边权总和。
此时,我们可以将军队分为两类 :
第一类 : 位于根节点的儿子节点
第二类 : 位于非根节点的儿子节点
对于第二类军队,我们让它保持不动即可,对于第一类军队,我们可以让它管辖自己所在子树的叶子节点,当然,我们也可以让它跨过根节点,管辖其所能到达的(不超过时间限制的),其它子树的叶子节点
这里有一条结论 : 对于一支第一类军队,如果这支军队不能跨过根节点并回到该节点,那么该节点必然由目前停留在这个节点上,且不能跨过根节点并回到该节点的,剩余时间最少的军队所管辖
根据这条结论,我们对于每个尚未被管辖的,根节点的子节点,查找是否有目前在该节点上并不能跨过根节点回到该节点的第一类军队,在这些军队中找剩余时间最少的管辖该节点,并将这支军队删除
对于剩余的第一类军队,我们可以先求出尚未被管辖的,根节点的子节点,然后,将军队按剩余时间 - 到达根节点的时间升序排列,将节点按到根节点的距离升序排列,贪心扫描一遍即可
[代码]
#include<bits/stdc++.h> using namespace std; #define MAXN 50010 #define MAXLOG 20 const long long INF = 1e15; struct edge { int to,w,nxt; } e[MAXN << 2]; struct info { int s; long long rest; bool used; } a[MAXN],b[MAXN]; int i,n,m,u,v,w,tot; int head[MAXN],pos[MAXN],depth[MAXN]; long long min_rest[MAXN]; int f[MAXN][MAXLOG]; long long dist[MAXN][MAXLOG]; long long sum,l,r,mid,ans; bool managed[MAXN]; inline bool cmp(info a,info b) { return a.rest < b.rest; } inline void addedge(int u,int v,int w) { tot++; e[tot] = (edge){v,w,head[u]}; head[u] = tot; } inline void dfs(int u) { int i,v,w; for (i = 1; i < MAXLOG; i++) { f[u][i] = f[f[u][i - 1]][i - 1]; dist[u][i] = dist[u][i - 1] + dist[f[u][i - 1]][i - 1]; } for (i = head[u]; i; i = e[i].nxt) { v = e[i].to; w = e[i].w; if (v == f[u][0]) continue; f[v][0] = u; dist[v][0] = w; depth[v] = depth[u] + 1; dfs(v); } } inline bool ok(int u) { int i,v,size = 0; bool res = true; if (managed[u]) return true; for (i = head[u]; i; i = e[i].nxt) { v = e[i].to; if (v == f[u][0]) continue; size++; res &= ok(v); } res &= size; if (depth[u] == 1 && !res) b[++tot] = (info){u,dist[u][0]}; return managed[u] = res; } inline bool check(long long mid) { int i,k,rest,len = 0,cnt = 0,now; static int tmp[MAXN],p[MAXN]; for (i = 1; i <= n; i++) { managed[i] = false; min_rest[i] = INF; } for (i = 1; i <= m; i++) tmp[i] = pos[i]; for (i = 1; i <= m; i++) { rest = mid; for (k = MAXLOG - 1; k >= 0; k--) { if (f[tmp[i]][k] == 0 || f[tmp[i]][k] == 1) continue; if (rest < dist[tmp[i]][k]) continue; rest -= dist[tmp[i]][k]; tmp[i] = f[tmp[i]][k]; } if (depth[tmp[i]] == 1 && rest - dist[tmp[i]][0] > 0) a[++len] = (info){tmp[i],rest - dist[tmp[i]][0]}; else managed[tmp[i]] = true; } tot = 0; if (ok(1)) return true; for (i = 1; i <= len; i++) { if (!managed[a[i].s] && a[i].rest < dist[a[i].s][0]) { if (a[i].rest < min_rest[a[i].s]) { min_rest[a[i].s] = a[i].rest; p[a[i].s] = i; } } } for (i = 1; i <= n; i++) { if (min_rest[i] != INF) { managed[i] = true; a[p[i]].used = true; cnt++; } } tot = 0; if (ok(1)) return true; sort(a + 1,a + len + 1,cmp); sort(b + 1,b + tot + 1,cmp); now = 1; for (i = 1; i <= tot; i++) { while (now <= len && (a[now].rest < b[i].rest || a[now].used)) now++; if (now > len || a[now].rest < b[i].rest || a[now].used) return false; now++; } return true; } int main() { scanf("%d",&n); for (i = 1; i < n; i++) { scanf("%d%d%d",&u,&v,&w); addedge(u,v,w); addedge(v,u,w); sum += (long long)w; } scanf("%d",&m); for (i = 1; i <= m; i++) scanf("%d",&pos[i]); dfs(1); l = 0; r = sum; ans = -1; while (l <= r) { mid = (l + r) >> 1; if (check(mid)) { r = mid - 1; ans = mid; } else l = mid + 1; } printf("%lld ",ans); return 0; }