CF 1039D You Are Given a Tree && CF1059E Split the Tree 的貪心解法
阿新 • • 發佈:2018-10-11
scanf sum turn namespace 試用 sin 這樣的 include clu
1039D 題意:
給你一棵樹,要求對給定鏈長於 k = 1, 2, 3, ..., n,求出最大的鏈剖分。
1059E 題意:
給你一棵帶權樹,要求對於一組給定的 L, W 求出最小完全豎鏈剖分滿足每條鏈點數不超過 L,權值和不超過 W。
顯然兩題是有共同點的,就是讓我們求滿足一定條件的樹的最值鏈剖分。
比較暴力的可以嘗試用 DP 計數,但是我不想深入 DP,因為方程比較復雜,思考起來不太容易。
很巧的是,這兩題可以用相似的貪心思想來做。
在思考具體細節之前,需要明確貪心的主要思想:在從下往上回溯的過程中,總是在合適條件下貪心地成鏈。
1039D
如果對於給定的 k 值可以快速求解,就可以用分塊的思想處理 k 不同時的情況。
怎樣求解給定的 k 呢?
還是貪心的做法。
在 dfs 回溯過程中,一旦當前點可以成鏈,就直接欽定下來 :)
具體操作過程中,需要對每個點記錄最長與次長子鏈,這樣一旦兩者和達到 k,就可以成鏈了。
證明略。
#include <bits/stdc++.h> using namespace std; const int N = 100000 + 5; const int BLOCK = 100; int n; int f[N][2]; vector<int> node; vector<int> g[N]; int fa[N]; void dfs(int u, int f = 1) { for (auto v: g[u]) { if (v != f) { dfs(v, u); } } node.push_back(u); fa[u] = f; } int solve(int k) { for (int i = 1; i <= n; i++) { f[i][0] = f[i][1] = 0; } int ret = 0; for (int u: node) { if (f[u][0] + f[u][1] + 1 >= k) { ret++; } else { if (f[fa[u]][0] < f[u][0] + 1) { f[fa[u]][1] = f[fa[u]][0]; f[fa[u]][0] = f[u][0] + 1; } else { f[fa[u]][1] = max(f[fa[u]][1], f[u][0] + 1); } } } return ret; } int main() { scanf("%d", &n); for (int i = 1; i < n; i++) { int u, v; scanf("%d %d", &u, &v); g[u].push_back(v); g[v].push_back(u); } dfs(1); int x = N, k = 1; while (x > BLOCK) { x = solve(k++); printf("%d\n", x); } for (int i = BLOCK; i >= 0; i--) { if (solve(k) != i || k > n) { continue; } int l = k, r = n + 1; while (r - l > 1) { int mid = (l + r) / 2; if (solve(mid) == i) { l = mid; } else { r = mid; } } while (k <= l) { printf("%d\n", i); k++; } } return 0; }
1059E
與上題不同的是,這題的鏈要求是豎直的,考慮從鏈底做貪心。
對於每個點,關註每條從子節點過來的鏈,並且貪心的選擇將當前點並入終點最高的鏈上。
如果沒有這樣的鏈,就直接根據題目條件選擇最高的鏈。
#include <bits/stdc++.h> using namespace std; const int N = 100000 + 5; int up[N]; int dep[N], path[N]; int fa[N][21]; int n, L; int w[N]; long long S; long long sumw[N]; vector<int> g[N]; void prepare(int u, int f = 0) { dep[u] = dep[f] + 1; sumw[u] = sumw[f] + w[u]; up[u] = u; fa[u][0] = f; for (int i = 1; i <= 20; i++) { fa[u][i] = fa[fa[u][i-1]][i-1]; } int lim = L-1; for (int i = 20; i >= 0; i--) { if (fa[up[u]][i] != 0 && (1 << i) <= lim && sumw[u] - sumw[fa[fa[up[u]][i]][0]] <= S) { up[u] = fa[up[u]][i]; lim -= (1 << i); } } for (int v: g[u]) { prepare(v, u); } } int solve(int u) { int ret = 0, best = -1; for (int v: g[u]) { ret += solve(v); if (path[v] != v) { if (best == -1 || dep[path[v]] < dep[best]) { best = path[v]; } } } if (best == -1) { ret++; best = up[u]; } path[u] = best; return ret; } int main() { scanf("%d %d %lld", &n, &L, &S); for (int i = 1; i <= n; i++) { scanf("%d", &w[i]); if (w[i] > S) { printf("-1\n"); return 0; } } for (int i = 2; i <= n; i++) { int p; scanf("%d", &p); g[p].push_back(i); } prepare(1); printf("%d\n", solve(1)); return 0; }
CF 1039D You Are Given a Tree && CF1059E Split the Tree 的貪心解法