洛谷 P7011 [CERC2013]Escape 題解
阿新 • • 發佈:2021-06-28
一、題目:
二、思路:
這道題又是一道非常新奇的思維題。我覺得能想到這種演算法的人真得很了不起。(反正我是看了題解才琢磨出來。)
我們考慮類似樹形 DP 的方式,從下向上轉移。對於每個子樹 \(x\),我們都維護一個集合 \(S_x\)。\(S_x\) 中的元素是二元組 \((a,b)\),代表如果你有至少 \(a\) 的血量,那麼你的血量可以增加 \(b\)。現在我們來考慮,如果對於 \(x\) 的任意一個兒子 \(y\),我們都已知了 \(S_y\),如何求出 \(S_x\)?
-
新建一個集合 \(S\)。
-
將所有 \(S_y\) 中的所有元素全部塞到 \(S\)
這樣肯定會使一些 \([a,a+b]\) 產生交集,我們將這些 \([a,a+b]\) 合併成大的區間。\((*)\)
-
嘗試將二元組 \((\min\{0,v_x \},v_x)\) 加入到集合 \(S\) 中。如果可以合併,就不斷的從小到大進行合併。直到不能合併為止。
最終怎麼判斷能不能 Escape 呢?我們可以新增一個點 \(t'\),讓 \(t'\) 與 \(t\) 相連,並把 \(t'\) 的權值設定成 \(+\infty\)。最終看一下 \(S_1\) 中有沒有元素的 \(b\) 大於等於 \(+\infty\)。
可以用 set 來維護集合。合併的時候用啟發式合併就可以了。
但是,總感覺 \((*)\) 這一步沒有必要。
事實上,\((*)\) 這一步的確沒必要。仔細想想,我們其實沒有必要使得所有的 \([a,a+b]\) 兩兩之間沒有交集。這並不影響我們的求解過程。
所以,我們只需要用小根堆來維護集合,而不必使用 set。
具體實現過程還必須看程式碼註釋。
三、程式碼:
#include <iostream> #include <cstdio> #include <cstring> #include <queue> #include <vector> using namespace std; #define FILEIN(s) freopen(s, "r", stdin); #define FILEOUT(s) freopen(s, "w", stdout) #define mem(s, v) memset(s, v, sizeof s) typedef pair<long long, long long> PLL; inline int read(void) { int x = 0, f = 1; char ch = getchar(); while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); } while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); } return f * x; } const int MAXN = 200005; const long long INF = 1e15; int n, t; long long val[MAXN]; vector<int>linker[MAXN]; priority_queue<PLL, vector<PLL>, greater<PLL> >q[MAXN]; void dfs(int x, int fa) { while (!q[x].empty()) q[x].pop(); for (auto &y : linker[x]) { if (y == fa) continue; dfs(y, x); if (q[y].size() > q[x].size()) swap(q[x], q[y]); // 啟發式合併 while (!q[y].empty()) { q[x].push(q[y].top()); q[y].pop(); } } PLL u = { 0, val[x] }, v; while (!q[x].empty() && (u.second < 0/*條件1*/ || q[x].top().first <= u.first + u.second/*條件2*/)) { v = q[x].top(); q[x].pop(); u = { max(u.first, v.first - u.second), u.second + v.second }; // 根據while語句的條件,如果靠的是條件2進入的迴圈,那麼max一定會取到u.first。 // 如果靠的是條件1進入的迴圈,那麼max一定會取到v.first-u.second,意思是提高v的門檻。 // (注意所有的v.first都是非負的。) } if (u.second >= 0) q[x].push(u); // 如果該條件不滿足,那麼q[x]一定是空的。 } int main() { int testdata = read(); while (testdata --) { n = read(); t = read(); for (int i = 1; i <= n; ++ i) val[i] = read(); for (int i = 1; i < n; ++ i) { int x = read(), y = read(); linker[x].push_back(y); linker[y].push_back(x); } ++ n; val[n] = INF; linker[t].push_back(n); linker[n].push_back(t); dfs(1, 0); if (!q[1].empty() && q[1].top().first == 0 && q[1].top().second >= INF) puts("escaped"); else puts("trapped"); for (int i = 1; i <= n; ++ i) linker[i].clear(); } return 0; }