题解:P5952 [POI2018] 水箱

posted on 2024-12-29 14:12:12 | under | source

将墙视为边,那么就是求给网格图赋值 \(a\) 的方案数,满足 \(a\le H\) 且对于一条边 \((u,v,w)\),假如 \(\max(a_u,a_v)>w\)\(a_u=a_v\)

试了下 dp、容斥等等,不好做,太生搬硬套了。考虑边权最大的一条边 \((u,v,w)\),对 \(a_u,a_v\) 进行分讨:

  1. \(a_u=a_v=x>w\):因为连最大的边都会溢出,那么值会一直传递到整个图,故方案数为 \(\max(0,H-w)\)
  2. \(a_u,a_v\le w\):则全图的点的值应 \(\le w\) 否则一定非法。考虑断掉这条边,假如图仍连通,那么将 \(H\gets w\),然后递归计算即可;否则,图将被分为两部分,显然互相独立,对两边分别计算乘起来即可。

删边不好做,不妨倒着做变成合并,用并查集维护即可。复杂度 \(O(n\log n)\)

代码

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

#define int long long
#define id(x, y) ((x - 1) * m + y)
const int N = 1e6 + 5, mod = 1e9 + 7;
int n, m, h, a[N], tot, x, fa[N], ans[N], lst[N];
struct edge{int u, v, w;} e[N];

inline int find(int u) {return fa[u] == u ? u : fa[u] = find(fa[u]);}
signed main(){
	cin >> n >> m >> h;
	for(int i = 1; i <= n * m; ++i) fa[i] = i, lst[i] = -1;
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j < m; ++j) scanf("%lld", &x), e[++tot] = {id(i, j), id(i, j + 1), x};
	for(int i = 1; i < n; ++i)
		for(int j = 1; j <= m; ++j) scanf("%lld", &x), e[++tot] = {id(i, j), id(i + 1, j), x};
	sort(e + 1, e + 1 + tot, [](edge A, edge B){return A.w < B.w;});
	for(int i = 1; i <= tot; ++i){
		int u = e[i].u, v = e[i].v, w = e[i].w;
		int fu = find(u), fv = find(v);
		if(fu ^ fv){
			ans[fu] = (ans[fu] + w - lst[fu]) % mod; 
			ans[fv] = (ans[fv] + w - lst[fv]) % mod;
			ans[fv] = ans[fv] * ans[fu] % mod;
			lst[fv] = w, fa[fu] = fv;
		}
		else{
			ans[fu] = (ans[fu] + w - lst[fu]) % mod;
			lst[fu] = w; 
		}
	}
	cout << (ans[find(1)] + h - e[tot].w) % mod;
	return 0;
}
posted @ 2026-01-15 08:18  Zwi  阅读(2)  评论(0)    收藏  举报