正题
题目大意
有一个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;
}