[bzoj5101][并查集]Powódź

18 篇文章 0 订阅

Description

在地面上有一个水箱,它的俯视图被划分成了n行m列个方格,相邻两个方格之间有一堵厚度可以忽略不计的墙,水
箱与外界之间有一堵高度无穷大的墙,因此水不可能漏到外面。已知水箱内每个格子的高度都是[0,H]之间的整数
,请统计有多少可能的水位情况。因为答案可能很大,请对10^9+7取模输出。两个情况不同当且仅当存在至少一个 方格的水位在两个情况中不同。

Input

第一行包含三个正整数n,m,H(n*m<=500000,1<=H<=10^9)。
接下来n行,每行m-1个整数a[i]j,表示(i,j)和(i,j+1)之间的墙的高度。
接下来n-1行,每行m个整数b[i]j,表示(i,j)和(i+1,j)之间的墙的高度。

Output

输出一行一个整数,即方案数模10^9+7的结果。

Sample Input

3 2 2

1

1

1

1 2

1 1

Sample Output

65

HINT

要么全部格子水位都是2,要么全部格子水位都在[0,1]之间,共1+2^6=65种情况。

题解

把墙按高度排序
设sum[i]表示i这个连通块在上一次访问到他的时候的水位及以下的方案数
扫到一堵墙的时候
如果这两个连通块没有连在一起
显然水位到这堵墙的高度时候他们就会连在一起了
设上一次访问到这两个连通块的水位分别是h1,h2
这堵墙的高度是H
这两个连通块并起来的答案就是 ( s u m [ u ] + H − h 1 ) ∗ ( s u m [ v ] + H − h 2 ) (sum[u]+H-h1)*(sum[v]+H-h2) (sum[u]+Hh1)(sum[v]+Hh2)
然后就没了…

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<ctime>
#define LL long long
#define mp(x,y) make_pair(x,y)
#define mod 1000000007
using namespace std;
const int dx[3]={0,0,1};
const int dy[3]={0,1,0};
inline int read()
{
	int f=1,x=0;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
inline void write(int x)
{
	if(x<0)putchar('-'),x=-x;
	if(x>9)write(x/10);
	putchar(x%10+'0');
}
inline void print(int x){write(x);printf(" ");}
int n,m,H;
struct Nod{int h,x,y,op;}w[1110000];int t1;
bool cmp(Nod n1,Nod n2){return n1.h<n2.h;}
int pt(int x,int y){return (x-1)*m+y;}
int fa[510000],lasth[510000];LL sum[510000];
int findfa(int x){return fa[x]==x?fa[x]:fa[x]=findfa(fa[x]);}
int main()
{
//	freopen("a.in","r",stdin);
//	freopen("a.out","w",stdout);
	n=read();m=read();H=read();
	for(int i=1;i<=n;i++)
		for(int j=1;j<m;j++)w[++t1].h=read(),w[t1].x=i,w[t1].y=j,w[t1].op=1;
	for(int i=1;i<n;i++)
		for(int j=1;j<=m;j++)w[++t1].h=read(),w[t1].x=i,w[t1].y=j,w[t1].op=2;
	sort(w+1,w+1+t1,cmp);
	for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)fa[pt(i,j)]=pt(i,j),sum[pt(i,j)]=1;
	int cnt=n*m,last=t1;
	for(int i=1;i<=t1;i++)
	{
		int u=w[i].x,v=w[i].y;
		int x=w[i].x+dx[w[i].op],y=w[i].y+dy[w[i].op];
		int u1=pt(u,v),u2=pt(x,y);
		int p=findfa(u1),q=findfa(u2);
		if(p!=q)
		{
			LL g1=sum[p],g2=sum[q];
			sum[q]=((w[i].h-lasth[p]+g1)*(w[i].h-lasth[q]+g2)%mod);
			lasth[q]=w[i].h;fa[p]=q;cnt--;
		}
		if(cnt==1){last=i;break;}
	}
	printf("%lld\n",(sum[findfa(pt(1,1))]+H-w[last].h)%mod);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值