JZOJ 6931. 【2020.12.26冬令营模拟】T3(二分)

JZOJ 6931. 【2020.12.26冬令营模拟】T3

题解

  • 考虑每条边的贡献拆开来看,那么式子可以用基本不等式化简求得最大值,但会发现每个点的贡献会算重,可以把每个点的贡献平均分给每条边。
  • 但这样子的正确性是无法保证的,不过有20分的特殊数据可以保证其正确性。
  • 另外,求最大值可以直接用三分。
  • 通过移项可以发现,当答案确定后,每个点拆给与其相连的边的比例系数确定,通过是否能够合理分配和单调性证明后可以直接使用二分解决。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
#define N 100010
#define ld long double
#define E 0.000000001
queue<int> q;
ld a[N], b[N * 2], f[N];
int len = 0, last[N], nxt[N * 2], to[N * 2];
int n, ok, fa[N], de[N], d[N], id[N];
void add(int x, int y, ld c) {
	to[++len] = y;
	nxt[len] = last[x];
	last[x] = len;
	b[len] = c;
}
void dfs(int k) {
	for(int i = last[k]; i; i = nxt[i]) if(to[i] != fa[k]) {
		de[k]++, fa[to[i]] = k, id[to[i]] = i, dfs(to[i]);
	}
}
void bfs(ld x) {
	ok = 1;
	for(int i = 1; i <= n; i++) f[i] = x * a[i];
	memset(d, 0, sizeof(d));
	for(int i = 1; i <= n; i++) if(d[i] == de[i]) q.push(i);
	while(!q.empty()) {
		int k = q.front();
		if(f[k] < 0) ok = 0;
		q.pop();
		d[fa[k]]++;
		f[fa[k]] -= b[id[k]] * b[id[k]] / 4.0 / f[k];
		if(d[fa[k]] == de[fa[k]]) q.push(fa[k]);
	}
}
int main() {
	int i, x, y; ld c;
	scanf("%d", &n);
	for(i = 1; i <= n; i++) scanf("%Lf", &a[i]);
	for(i = 1; i < n; i++) {
		scanf("%d%d%Lf", &x, &y, &c);
		add(x, y, c), add(y, x, c);
	}
	dfs(1);
	ld l = 0, r = 40000, ans;
	while(l <= r) {
		ld mid = (l + r) / 2.0;
		bfs(mid);
		if(!ok) l = mid + E; else r = mid - E, ans = mid;
	}
	printf("%Lf
", ans);
	return 0;
}

自我小结

  • 较为熟练地运用数学知识,虽然不是正解,但合理拿到了部分分。
  • 与此同时需要注意的是,程序实现数学方法不一定要按部就班,也许会有捷径,如对勾函数的极值可以直接三分。
原文地址:https://www.cnblogs.com/LZA119/p/14279480.html