题解:AT_cf16_exhibition_final_e Water Distribution

posted on 2024-10-18 02:31:15 | under | source

首先 \(\max(0,l-dis(x,y))\) 不太好看,不妨放宽限制,不与 \(0\)\(\max\),这显然不会有影响:现答案肯定不优于原答案,又因为最优情况中肯定不会有 \(l\le dis(x,y)\),所以包含最优解。

然后让传递水的两个城市 \(x,y\) 连无向边,权值为负就是 \(y\)\(x\) 传水。不可能有环,因为可以把环中最大的边去掉,让那份水沿着更小的路径传递。

所以最终构成森林,对于一棵树 \(T\),答案上界就是 \(\frac{\sum a-\sum e}{|T|}\),也就是平均数,而且可以达到该上界,首先随便传水让每个点都到达平均数,然后若一条边被经过多次那么可以直接合并为一次。容易发现,该过程中不会有 \(a\) 变成负数(当然是在原来合法的情况下)。

看到 \(n\le 16\) 直接状压,先暴力枚举每个连通块,然后 \(\min(\sum e)\) 就是最小生成树,最后做一次 dp 合并为森林。

复杂度 \(O(2^nn^2\log n^2+3^n)\),可以通过。

代码

#include<bits/stdc++.h>
using namespace std;

#define db double
const int N = 16;
const db inf = 1e18;
int n, fa[N], m;
db x[N], y[N], v[N], f[1 << N], g[1 << N];
struct edge{int u, v; db w;} e[N * N + 5];

inline int find(int u) {return fa[u] == u ? u : fa[u] = find(fa[u]);}
inline db dist(int a, int b) {return sqrt((x[a] - x[b]) * (x[a] - x[b]) + (y[a] - y[b]) * (y[a] - y[b]));}
signed main(){
	cin >> n;
	for(int i = 0; i < n; ++i) scanf("%lf%lf%lf", &x[i], &y[i], &v[i]); 
	for(int S = 1; S < 1 << n; ++S){
		m = 0;
		for(int i = 0; i < n; ++i)
			if((S >> i) & 1){
				fa[i] = i, f[S] += v[i];
				for(int j = i + 1; j < n; ++j)
					if((S >> j) & 1)
						e[++m] = {i, j, dist(i, j)};
			}
		sort(e + 1, e + 1 + m, [](edge A, edge B){return A.w < B.w;});
		for(int i = 1; i <= m; ++i){
			int fu = find(e[i].u), fv = find(e[i].v);
			if(fu != fv) fa[fu] = fv, f[S] -= e[i].w;
		}
		f[S] /= __builtin_popcount(S);
	}
	g[0] = inf;
	for(int S = 1; S < 1 << n; ++S)
		for(int S2 = S; S2; S2 = (S2 - 1) & S)
			g[S] = max(g[S], min(g[S ^ S2], f[S2]));
	printf("%.9lf", g[(1 << n) - 1]);
	return 0;
}
posted @ 2026-01-15 08:16  Zwi  阅读(3)  评论(0)    收藏  举报