[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 }

猜你喜欢

转载自www.cnblogs.com/HocRiser/p/10259424.html