题解:AGC023F 01 on Tree / SS230909A 数据恢复
题解:AGC023F 01 on Tree / SS230909A 数据恢复
他说题目来源是 gym292796J,但是我没有权限???可能是太菜了。http://www.accoders.com/problem.php?cid=4501&pid=0
AtCoder 链接:F - 01 on Tree
题目描述
给定一棵树,树上每个点有非负点权 \(a,b\),求一个操作顺序,使得总是先选父亲在选儿子,同时价值和:每个点选的时候的 \(b\) 乘上未选的点的 \(a\) 的和,求和。最大化价值和。\(n\leq 10^5\)。
AGC023F 可以轻易转化为这个问题。
solution
菊花
考虑贪心,使用邻项交换法:假设操作中间有两个点 \((a_1,b_1),(a_2,b_2)\),现在这两个东西的价值是 \(b_1a_2\),swap 一下是 \(b_2a_1\),如果你说他的价值和最大,那么就有 \(b_1a_2>b_2a_1\implies\frac{a_1}{b_1}<\frac{a_2}{b_2}\),欢聚话来说我们只要将所有数字按照 \(a/b\) 排序就好了。
一般树
那么我们还是先按照 \(a/b\) 排序,但是会有一些点违背限制,怎么办呢?我们每次找出全局最小值,如果它的父亲还在,那么将它和父亲合并成一个大点并立刻计算贡献,否则删掉并计算贡献。
因为 \(\frac{a}{b}\leq \frac{a+c}{b+d}\leq\frac{b}{d}\),就是说这样合并以后这个不合法的全局最小值就会跑到后面去,等待以后接受进一步的审判。
合并可以使用并查集,最小值用堆维护。
代码实现
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <numeric>
#include <queue>
#include <set>
#include <vector>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stderr, ##__VA_ARGS__)
#else
#define debug(...) void(0)
#endif
typedef long long LL;
struct frac {
LL x, y;
bool operator<(frac b) const { return x * b.y < b.x * y; }
frac operator+(frac b) { return frac{x + b.x, y + b.y}; } // special
LL operator*(frac b) { return b.x * y; }
};
frac a[1 << 17];
template <int N>
struct dsu {
int fa[N + 10], siz[N + 10], cnt;
explicit dsu(int n = N) : cnt(n) {
iota(fa + 1, fa + n + 1, 1), fill(siz + 1, siz + n + 1, 1);
}
int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }
void merge(int x, int y) {
if (x = find(x), y = find(y), x != y)
cnt--, fa[y] = x, siz[x] += siz[y], a[x] = a[x] + a[y];
}
};
int n, fa[1 << 17];
dsu<1 << 17> s;
LL ans = 0;
bool vis[1 << 17];
set<pair<frac, int>> q;
int main() {
scanf("%d", &n);
for (int i = 2; i <= n; i++) scanf("%d", &fa[i]);
for (int i = 1; i <= n; i++) scanf("%lld%lld", &a[i].x, &a[i].y);
for (int i = 1; i <= n; i++) q.insert({a[i], i});
vis[0] = 1;
frac out = {0, 0};
for (int i = 1; i <= n; i++) {
int u = q.begin()->second, f = s.find(fa[u]);
q.erase(q.begin());
if (vis[f])
ans += out * a[u], out = out + a[u], vis[u] = 1;
else {
q.erase(q.find({a[f], f}));
ans += a[f] * a[u];
s.merge(f, u);
q.insert({a[f], f});
}
}
printf("%lld\n", ans);
return 0;
}
本文来自博客园,作者:caijianhong,转载请注明原文链接:https://www.cnblogs.com/caijianhong/p/solution-SS230909A.html
浙公网安备 33010602011771号