「洛谷5290」「LOJ3052」「十二省联考 2019」春节十二响【启发式合并】

题目链接

【洛谷传送门】
【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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值