[題目鏈接]
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\n",ans); return 0; }