其他分享
首页 > 其他分享> > 【最小生成树】水箱(P5952)

【最小生成树】水箱(P5952)

作者:互联网

正题

P5952


题目大意

有一个n*m的网格,每个网格之间有一个板,给出每个板的高度(边界有一个高度为 ∞ \infty ∞ 的墙),在每个网格中注水(必须是非负整数),使得两个高度不等且相邻的网格之间有一个大于两边高度的板(可以理解为水的自由流动,如果没有板阻挡会过去),若最高高度为H,问合法的注水方案数


解题思路

考虑每一个网格网上注水会先越过哪个板,与另一个点高度同步

对每个板,建一条连接两个点的边,然后跑最小生成树,那么往上注水然后与别的格子同步一定之和这棵树上的边有关

证明:

如果存在一个点y和x相连,但是x和y的边不在最小生成树内,如果x在注水到x-y的高度之前都没有和y同步,那么最小生成树上x到y的路径最大权值就大于x-y的高度,不符合最小生成树的定义,所以不存在这样的情况

设 h x h_x hx​ 为当前状态下,x 所在连通块的最高的板, a n s x ans_x ansx​ 为当前状态下,x 所在连通块到达最高的板的高度之前的方案数(因为超过最高的板后,可能注的水后超过后面增加的板)

对于每一次连边,设 z 为当前板的高度,那么有 a n s = ( a n s x + z − h x ) × ( a n s y + z − h y ) ans=(ans_x+z-h_x)\times (ans_y+z-h_y) ans=(ansx​+z−hx​)×(ansy​+z−hy​),即两边的水可以选择 h + 1 ∼ z h+1\sim z h+1∼z 的高度,所以方案数加上z-h

最后得到的答案加上全部水覆盖到H的方案数即可


code

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
#define N 500500
#define mp make_pair
#define fs first
#define sn second
#define mod 1000000007
using namespace std;
ll n,m,w,H,x,y,z,h[N],fa[N],ans[N];
pair<int,pair<int,int> >a[N<<1];
ll g(ll x,ll y)
{
	return (x-1)*m+y;
}
ll find(ll x)
{
	return x==fa[x]?x:fa[x]=find(fa[x]); 
}
int main()
{
	scanf("%lld%lld%lld",&n,&m,&H);
	for(ll i=1;i<=n;++i)
		for(ll j=1;j<m;++j){
			scanf("%lld",&x);
			a[++w]=mp(x,mp(g(i,j),g(i,j+1)));
		}
	for(ll i=1;i<n;++i)
		for(ll j=1;j<=m;++j){
			scanf("%lld",&x);
			a[++w]=mp(x,mp(g(i+1,j),g(i,j)));
		}
	sort(a+1,a+1+w);
	for(ll i=1;i<=n;++i)
		for(ll j=1;j<=m;++j){
			x=g(i,j);
			fa[x]=x;
			ans[x]=1;
		}
	for(ll i=1;i<=w;++i){
		x=find(a[i].sn.fs);
		y=find(a[i].sn.sn);
		z=a[i].fs;
		if(x==y)continue;
		fa[x]=y;
		ans[y]=(ans[x]+z-h[x]+mod)%mod*((ans[y]+z-h[y]+mod)%mod)%mod;
		h[y]=z;
	}
	x=find(1);
	printf("%lld",(ans[x]+H-h[x])%mod);
	return 0;
}

标签:ll,高度,最小,水箱,fa,P5952,ans,define,mod
来源: https://blog.csdn.net/ssllyf/article/details/121711092