题单
A
咕咕咕
B
咕咕咕
C
咕咕咕
D春节十二响
vjudge
luogu
(Day -1 过的qwq)
题意:一棵树,节点有点权,将节点分成任意个集合,要求每个集合内的节点不是祖先——后代关系,问(每个集合中的(点的最大值)之和)最小是多少。
很妙的启发式合并。
考虑如果这是个链而且根左右两条小链,显然可以将两边的链各扔进一个堆,每次取堆首,并加上两者较大值(感性理解,用一边最大的消耗另一边的最大,是贡献最少)。
然后扩展到树上,每个点开一个堆,这就叫树上启发式合并。
现在还不行,因为这东西会被卡成 \(O(n^2\log n)\)。
然后才是启发式的常用 trik:
将子树大小小的往大的里插。
这东西就类似于树剖那个重儿子,直接把 \(O(n^2)\) 变成 \(O(n\log n)\)。
而且代码只需加一行:
点击查看代码
if (q[id[u]].size() < q[id[v]].size()) swap(id[u], id[v]);
然后得到了启发式合并。
哦哦下标那里有个 trik,是说我们要交换两个堆,直接换是 \(O(\text {size})\) 的,但是可以每个点存个对应堆的下标,然后直接换下标……
做完了……
点击查看代码
#include <bits/stdc++.h>
#define dbg(x) cout << #x << '=' << x << endl
#define rep(i, l, r) for (int i = (l); i <= (r); i++)
#define frep(i, r, l) for (int i = (r); i >= (l); i--)
#define int long long
using namespace std;const int N = 2e5 + 10;int n, cnt;
int a[N], id[N];
vector<int> e[N];
priority_queue<int> q[N];
int b[N], pos;int Get(int x) {int p = q[x].top();q[x].pop(); return p;
}void Dfs(int u) {id[u] = ++cnt;int ax, ay;for (int v : e[u]) {Dfs(v);if (q[id[u]].size() < q[id[v]].size()) swap(id[u], id[v]);pos = 0;while (!q[id[v]].empty()) b[++pos] = max(Get(id[u]), Get(id[v])); rep(j, 1, pos) q[id[u]].push(b[j]);}q[id[u]].push(a[u]);
}void work()
{cin >> n;rep(i, 1, n) cin >> a[i];rep(i, 2, n) {int f; cin >> f;e[f].push_back(i);}Dfs(1);int sum = 0;while (!q[id[1]].empty()) sum += Get(id[1]);cout << sum << "\n";
}signed main()
{std::ios::sync_with_stdio(false);cin.tie(0), cout.tie(0);int T = 1, opinput = 0;if (opinput) cin >> T;while (T--) work();return 0;
}
其实没什么大结论的题,感觉比较板……
希望能场切 这种吧。
E
咕咕咕
F
咕咕咕
G
咕咕咕
H
咕咕咕
I
咕咕咕