类似于Kruskal重构树上DP。
相邻点之间连一条权为墙高的边,将边从小到大排序,每次合并一条边连接的两个连通块。
h[i]表示连通块i中最高的墙,g[i]表示所有水位都不超过h[i]的情况下的方案数。最后答案就是g[1]+H-h[1]。
当某处水位高于h[i]时,水就会往四周流淌,所以合并边权为w的边连接的两个连通块时,h=w,g=(g1+w-h1)*(g2+w-h2)。
意思就是,新的方案数,等于两个连通块在水位均不超过w时的方案数之积。一个连通块水位不超过w的方案数,等于水位不超过h1的方案数,加所有区域水位相等且都大于h1的方案数,即g1+w-h1。
1 #include<cstdio> 2 #include<algorithm> 3 #define F(A,B) ((A-1)*m+B) 4 #define rep(i,l,r) for (int i=(l); i<=(r); i++) 5 typedef long long ll; 6 using namespace std; 7 8 const int N=500010,mod=1000000007; 9 int n,m,H,x,tot,f[N],g[N],h[N]; 10 struct E{ int u,v,w; }e[N<<1]; 11 bool operator <(const E &a,const E &b){ return a.w<b.w; } 12 int get(int x){ return (f[x]==x) ? x : f[x]=get(f[x]); } 13 14 int main(){ 15 freopen("bzoj5101.in","r",stdin); 16 freopen("bzoj5101.out","w",stdout); 17 scanf("%d%d%d",&n,&m,&H); 18 rep(i,1,n) rep(j,1,m-1) scanf("%d",&x),e[++tot]=(E){F(i,j),F(i,j+1),x}; 19 rep(i,1,n-1) rep(j,1,m) scanf("%d",&x),e[++tot]=(E){F(i,j),F(i+1,j),x}; 20 sort(e+1,e+tot+1); 21 rep(i,1,n*m) f[i]=i,g[i]=1,h[i]=0; 22 rep(i,1,tot){ 23 int x=get(e[i].u),y=get(e[i].v); 24 if (x==y) continue; 25 g[y]=1ll*(g[x]+e[i].w-h[x])*(g[y]+e[i].w-h[y])%mod; 26 h[y]=e[i].w; f[x]=y; 27 } 28 printf("%d\n",(g[get(1)]+H-h[get(1)])%mod); 29 return 0; 30 }