「洛谷5290」「LOJ3052」「十二省聯考 2019」春節十二響【啟發式合並】
阿新 • • 發佈:2019-04-11
power 啟發式合並 max pop define code back == 最小
題目鏈接
【洛谷傳送門】
【LOJ傳送門】
題目大意
給定一棵樹,每次選取樹上的一個點集,要求點集中的每個點不能是另一個點的祖先,選出點集的代價為點集中權值最大點的權值,問將所有點都選一遍的最小代價為多少。
(題目大意來自洛谷題解某一篇)
題解
分析一下這一道題目。
首先,因為不能存在祖先關系,那麽在一條鏈上的所有點一定是要分開來取的。
那麽很顯然,根必須一個點一個集合,那麽在遞歸子樹,同樣的操作,把子樹獨立的遞歸,然後合並子樹內的最大值。
代碼
#include <bits/stdc++.h> #define ms(a, b) memset(a, b, sizeof(a)) #define ll long long #define ull unsigned long long #define ms(a, b) memset(a, b, sizeof(a)) #define inf 0x3f3f3f3f #define db double #define Pi acos(-1) #define eps 1e-8 #define pq priority_queue #define N 200005 using namespace std; template <typename T> T power(T x, T y, T mod) { x %= mod; T res = 1; for (; y; y >>= 1) { if (y & 1) res = (res * x) % mod; x = (x * x) % mod; } return res; } template <typename T> void read(T &x) { x = 0; T fl = 1; char ch = 0; for (; ch < '0' || ch > '9'; ch = getchar()) if (ch == '-') fl = -1; for (; ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48); x *= fl; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } vector<int> g[N]; int n, val[N], id[N], tot; pq<int> q[N]; void merge(int &a, int &b) { if (q[a].size() < q[b].size()) swap(a, b); int tot = 0; static int c[N]; while (!q[b].empty()) c[++ tot] = max(q[a].top(), q[b].top()), q[a].pop(), q[b].pop(); for (int i = 1; i <= tot; i ++) q[a].push(c[i]); } void dfs(int u) { id[u] = ++ tot; for (int i = 0; i < g[u].size(); i ++) { int v = g[u][i]; dfs(v); merge(id[u], id[v]); } q[id[u]].push(val[u]); } int main() { tot = 0; read(n); for (int i = 1; i <= n; i ++) read(val[i]); for (int i = 2, fa; i <= n; i ++) read(fa), g[fa].push_back(i); dfs(1); ll ans = 0; while (!q[id[1]].empty()) { ans += q[id[1]].top(); q[id[1]].pop(); } writeln(ans); return 0; }
「洛谷5290」「LOJ3052」「十二省聯考 2019」春節十二響【啟發式合並】