洛谷 P1084 疫情控制 noip2013D2T3
題目描述
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
說明
【輸入輸出樣例說明】
第一支軍隊在 22 號點設立檢查點,第二支軍隊從 22 號點移動到33 號點設立檢查點,所需時間為 33 個小時。
【資料範圍】
保證軍隊不會駐紮在首都。
對於 20%的資料,2≤n≤10;
對於 40%的資料,2 ≤n≤50,0<w <10^5;
對於 60%的資料,2 ≤ n≤1000,0<w <10^6;
對於 80%的資料,2 ≤ n≤10,000;
對於 100%的資料,2≤m≤n≤50,000,0<w <10^9。
NOIP 2012 提高組 第二天 第三題
errrrrrrrrrrrrrrrrrrrra 我要被這道題惡星死了!!!! 全是細節!!!我寫了一個小時調了三個多小時.. 我他媽真實爆哭
這道題思路還是比較簡單的 首先這道題求最少時間 二分性顯然 所以考慮二分
首先可以發現軍隊越向上走能夠控制的點就越多 所以我們可以貪心判斷軍隊是否可以跳到根節點
二分最短時間為$tim$ 將他跳到根節點之後的剩餘時間記做$res$ 將軍隊往上跳時經過的根節點的兒子節點記做$top$
如果這個點不能夠跳到根節點 即$res <0$ 那麼他只需要儘量往上跳即可 在他最後跳到的點打上$vis$標記 因為它沒有跨越根節點支援其他子樹的能力 這個顯然是用倍增優化
其他具有支援能力的加入一個數組 在做完這些之後將已經打上的標記上傳 條件是所有的兒子都被控制了 那麼他自己就被控制了
那麼怎麼判斷剩餘是否合法呢
將所有沒打標記的點加入另一個數組 兩陣列分別排序用雙指標一一比較即可
但是有一種特殊情況 如果這個點能夠跳到根節點 但是他的剩餘時間都不夠使自己返回$top$ 那麼他還是呆在$top$好了
對於這種特例還有種特例 就是他的下方軍隊無法跳到根節點 並且已經控制了$top$這條路徑 那麼他就不需要呆在$top$了 可以對其他點產生貢獻 即使貢獻很小
最後如果根節點所有的兒子都可以被打上標記 那麼他就是合法的 更多細節寫在程式碼裡了
程式碼
#include <bits/stdc++.h> #define il inline #define rg register #define oo 5e13 using namespace std; typedef long long ll; const int N = 5 * 1e4 + 5; const int P = 25; int tot, head[N], nex[2 * N], tov[2 * N], val[2 * N]; int l[N], ys[N], n, m, a[N], cnt, cn; ll ans, S, dis[N], len[N][P], anc[N][P]; bool vis[N]; struct node { int w, id; bool use; }f[N], h[N]; il bool cmp(const node & a, const node & b) { return a.w < b.w; } il int read( ) { int t = 1, ans = 0; char x; x = getchar( ); while(x < '0' || x > '9') { if(x == '-') t = -1; x = getchar( ); } while(x >= '0' && x <= '9') { ans = ans * 10 + x - '0'; x = getchar( ); } return ans * t; } il void add(int u, int v, int w) { tot ++; nex[tot] = head[u]; tov[tot] = v; head[u] = tot; val[tot] = w; } il void dfs(int u, int fa) { for(rg int p = 1;p <= 15;p ++) len[u][p] = len[u][p - 1] + len[anc[u][p - 1]][p - 1], anc[u][p] = anc[anc[u][p - 1]][p - 1]; for(rg int i = head[u];i;i = nex[i]) { int v = tov[i]; if(v == fa) continue; len[v][0] = val[i], anc[v][0] = u; dis[v] = dis[u] + val[i]; l[v] = u == 1 ? val[i] : l[u];//top到根節點的路徑的值 ys[v] = u == 1 ? v : ys[u];//對應每個節點的top dfs(v, u); } } il void Init( ) { n = read( ); for(rg int i = 1;i < n;i ++) { int u, v, w; u = read( ), v = read( ), w = read( ); add(u, v, w); add(v, u, w); S += w; } m = read( ); for(rg int i = 1;i <= m;i ++) a[i] = read( ); for(rg int i = 1;i <= n;i ++) for(rg int j = 1;j <= 15;j ++) len[i][j] = oo; dfs(1, 0); } il void make_tag(int u, int fa) { int r = 0, x = 0; for(rg int i = head[u];i;i = nex[i]) { int v = tov[i]; if(v == fa) continue; make_tag(v, u); r ++; x += vis[v]; } if(r == x && r) vis[u] = true; } il void clear( ) { for(rg int i = 1;i <= n;i ++) vis[i] = false; cnt = 0; cn = 0; } il bool check(ll tim) { for(rg int i = 1;i <= m;i ++) { int top = a[i]; ll res = tim - dis[top]; if(res > 0 && res < l[top]) {f[++ cnt].w = res; f[cnt].id = ys[top]; f[cnt].use = true; continue;}//打他是特例的標記 if(res > 0) {f[++ cnt].w = res; f[cnt].id = ys[top]; f[cnt].use = false; continue;} ll t = tim; for(rg int p = 15;p >= 0;p --) { if(t >= len[top][p] && len[top][p] && anc[top][p] && anc[top][p] != 1) //不能跳到根節點 長度非零 祖先非零 t -= len[top][p], top = anc[top][p];//先減再跳 否則len會變化 } vis[top] = true; } make_tag(1, 0); for(rg int i = head[1];i;i = nex[i]) { int v = tov[i]; if(vis[v]) continue; h[++ cn].w = val[i]; h[cn].id = v; } sort(f + 1, f + cnt + 1, cmp); sort(h + 1, h + cn + 1,cmp); if(! cn) return true; int h1 = 1, h2 = 1; while(h2 <= cn && h1 <= cnt) { if(vis[h[h2].id]) { h2 ++; continue;//如果已經被標記了 就重新開始判斷 因為可能有不止一個h2對應的被打了標記 } if(! vis[f[h1].id] && f[h1].use) { vis[f[h1].id] = true, h1 ++; continue;//同理 } if(h[h2].w <= f[h1].w) vis[h[h2].id] = true, h1 ++, h2 ++; else h1 ++; } for(rg int i = head[1];i;i = nex[i]) { int v = tov[i]; if(! vis[v]) return false; } return true; } il void Solve( ) { ll l = 0, r = S, ans = -1; while(l <= r) { ll mid = l + r >> 1; clear( ); if(check(mid)) ans = mid, r = mid - 1; else l = mid + 1; } printf("%lld\n", ans); } int main( ) { Init( ); Solve( ); }