【洛谷】1084:疫情控制【倍增】【二分】
P1084 疫情控制
題目描述
H 國有 n個城市,這 n 個城市用n−1條雙向道路相互連通構成一棵樹,1號城市是首都,也是樹中的根節點。
H國的首都爆發了一種危害性極高的傳染病。當局為了控制疫情,不讓疫情擴散到邊境城市(葉子節點所表示的城市),決定動用軍隊在一些城市建立檢查點,使得從首都到邊境城市的每一條路徑上都至少有一個檢查點,邊境城市也可以建立檢查點。但特別要注意的是,首都是不能建立檢查點的。
現在,在 H 國的一些城市中已經駐紮有軍隊,且一個城市可以駐紮多個軍隊。一支軍隊可以在有道路連線的城市間移動,並在除首都以外的任意一個城市建立檢查點,且只能在一個城市建立檢查點。一支軍隊經過一條道路從一個城市移動到另一個城市所需要的時間等於道路的長度(單位:小時)。
請問最少需要多少個小時才能控制疫情。注意:不同的軍隊可以同時移動。
輸入輸出格式
輸入格式:
第一行一個整數n,表示城市個數。
接下來的 n−1 行,每行3個整數,u,v,w,每兩個整數之間用一個空格隔開,表示從城市 u到城市v 有一條長為 w 的道路。資料保證輸入的是一棵樹,且根節點編號為 1。
接下來一行一個整數 m,表示軍隊個數。
接下來一行 m個整數,每兩個整數之間用一個空格隔開,分別表示這 m 個軍隊所駐紮的城市的編號。
輸出格式:
一個整數,表示控制疫情所需要的最少時間。如果無法控制疫情則輸出−1。
輸入輸出樣例
輸入樣例#1: 複製4
1 2 1
1 3 2
3 4 3
2
2 2
輸出樣例#1: 複製
3
說明
【輸入輸出樣例說明】
第一支軍隊在 2 號點設立檢查點,第二支軍隊從 2 號點移動到3 號點設立檢查點,所需時間為 3 個小時。
【資料範圍】
保證軍隊不會駐紮在首都。
對於 20%的資料,2≤n≤10;
對於 40%的資料,2≤n≤50,0<w<105;
對於 60%的資料,2≤n≤1000,0<w<106;
對於 80%的資料,2≤n≤10,000;
對於 100%的資料,2≤m≤n≤50,000,0<w<109。
NOIP 2012 提高組 第二天 第三題
Solution
感天動地!!終於調出來了!!想是好想,程式碼實現細節好多aaaaa!!!!QAQ
二分答案,check中間將軍隊倍增貪心向上跳,如果可以跳到根節點,那麼剩下還可以跳的值意味著它可以轉移到根節點的其它兒子去,如果不能跳到根節點意味著它必須固定在能跳到的最高位置,打標記。
統計標記,如果子樹全都有標記那麼它也有標記。
統計哪些根節點的兒子沒有被標記,這些節點就有可能從之前統計的可以轉移出來的這些點移過來。
所以排序後雙指標掃描,如果在統計標記後原來的位置都還沒有被打標記,那麼這個位置就不能移出去了,不然如果滿足移出去的路徑長度,那麼就給移出去的位置打標記。
錯的好慘:
for(int p = 20; p >= 0; p --) if(l + len[u][p] <= mid && jum[u][p] != 0) l += len[u][p], u = jum[u][p];
這裡應該先更新長度再跳,之前一直先跳再更新QAQ
#include<bits/stdc++.h> #define LL long long using namespace std; int read() { int x = 0, t = 0; char ch = getchar(); while(!isdigit(ch)) t |= (ch == '-'), ch = getchar(); while(isdigit(ch)) x = x * 10 + ch - '0', ch = getchar(); return x *= (t ? -1 : 1); } struct Node { int v, nex, w; } Edge[500005]; int h[50005], stot; void add(int u, int v, int w) { Edge[++stot] = (Node) {v, h[u], w}; h[u] = stot; } int n, m; int jum[50005][21]; LL len[50005][21], dis[50005]; void dfs(int u, int f) { jum[u][0] = f; for(int p = 1; p <= 20; p ++) jum[u][p] = jum[jum[u][p - 1]][p - 1], len[u][p] = len[u][p - 1] + len[jum[u][p - 1]][p - 1]; for(int i = h[u]; i; i = Edge[i].nex) { int v = Edge[i].v; if(v == f) continue; len[v][0] = Edge[i].w; dis[v] = dis[u] + Edge[i].w; dfs(v, u); } } int tag[50005]; void get_tag(int u, int f) { int d = 0; int p = 1; for(int i = h[u]; i; i = Edge[i].nex) { int v = Edge[i].v; if(v == f) continue; get_tag(v, u); d ++; p &= tag[v]; } if(p && d && u != 1) tag[u] = 1; } int pos[50005]; struct Point { int id; LL w; } p[50005], q[50005]; bool cmp(Point a, Point b) { return a.w < b.w; } bool check(int mid) { int sd = 0, sb = 0; memset(tag, 0, sizeof(tag)); for(int i = 1; i <= m; i ++) { int u = pos[i], l = 0; for(int p = 20; p >= 0; p --) if(l + len[u][p] <= mid && jum[u][p] != 0) l += len[u][p], u = jum[u][p]; if(u != 1) tag[u] = 1; else { int x = pos[i]; q[++sb].w = mid - l; for(int p = 20; p >= 0; p --) if(jum[x][p] > 1) x = jum[x][p]; q[sb].id = x; } } get_tag(1, 0); for(int i = h[1]; i; i = Edge[i].nex) { int v = Edge[i].v; if(tag[v]) continue; p[++sd].w = dis[v]; p[sd].id = v; } sort(q + 1, q + 1 + sb, cmp); sort(p + 1, p + 1 + sd, cmp); int l = 1; for(int i = 1; i <= sb; i ++) { if(!tag[q[i].id]) tag[q[i].id] = 1; else if(q[i].w >= p[l].w) tag[p[l].id] = 1; while(tag[p[l].id] && l <= sd) ++ l; } return l > sd; } LL sum; LL erfen() { LL l = 0, r = sum, ans; while(l <= r) { int mid = (l + r) >> 1; if(check(mid)) ans = mid, r = mid - 1; else l = mid + 1; } return ans; } int main() { n = read(); for(int i = 1; i < n; i ++) { int u = read(), v = read(), w = read(); add(u, v, w); add(v, u, w); } m = read(); int tmp = 0; for(int i = h[1]; i; i = Edge[i].nex) tmp ++; if(tmp > m) { puts("-1"); return 0; } dfs(1, 0); for(int i = 1; i <= n; i ++) sum += dis[i]; for(int i = 1; i <= m; i ++) pos[i] = read(); LL ans = erfen(); printf("%lld", ans); return 0; }