1. 程式人生 > >[BZOJ5101][POI2018]Powódź(並查集)

[BZOJ5101][POI2018]Powódź(並查集)

類似於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 }