P5952 [POI2018]水箱

D e s c r i p t i o n Description Description

有一个大小为 n × m n\times m n×m的方格,周围是无限高的墙,内侧的墙则有自己的高度,规定水位高的地方的水会流向水位低的地方,求所有水位高度的可能性,答案对 1 0 9 + 7 10^9+7 109+7取模

数据范围: n × m ≤ 5 × 1 0 5 n\times m\leq 5\times 10^5 n×m5×105


S o l u t i o n Solution Solution

考虑每道墙依次从矮到高开通(不可能有高的墙先开通,因为在此之前水肯定已经流了)

假设开启了一道墙,这个墙两面的答案分别为 a n s [ x ] , a n s [ y ] ans[x],ans[y] ans[x],ans[y],它们分别处理的是 0 ∼ h [ x ] , 0 ∼ h [ y ] 0\sim h[x],0\sim h[y] 0h[x],0h[y]的情况

那么如果把这面高度为 e [ i ] . h e[i].h e[i].h墙开通,则左边的方案为 ( a n s [ x ] + e [ i ] . h − h [ x ] ) (ans[x]+e[i].h-h[x]) (ans[x]+e[i].hh[x]),表示新增了这么多的高度
右边的同理,合并起来即为两数相乘

时间复杂度: O ( n m l o g n m ) O(nmlognm) O(nmlognm)


C o d e Code Code

#include
#include
#include
#define LL long long
#define id(i,j) (i-1)*m+j
#define mod 1000000007 
using namespace std;int n,m,tot,f[500010];
struct node{int from,to;LL h;}e[1000010];
inline bool cmp(node x,node y) {return x.h<y.h;} 
inline int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
LL x,H,Ans[500010],h[500010];
inline LL read()
{
	char c;LL d=1,f=0;
	while(c=getchar(),!isdigit(c)) if(c=='-') d=-1;f=(f<<3)+(f<<1)+c-48;
	while(c=getchar(),isdigit(c)) f=(f<<3)+(f<<1)+c-48;
	return d*f;
}
signed main()
{
	n=read();m=read();H=read();
	for(register int i=1;i<=n;i++)
	 for(register int j=1;j<m;j++)
	{
		x=read();
		e[++tot]=(node){id(i,j),id(i,j+1),x};
	}
	for(register int i=1;i<n;i++)
	 for(register int j=1;j<=m;j++)
	{
		x=read();
		e[++tot]=(node){id(i,j),id(i+1,j),x};
	}
	for(register int i=1;i<=n*m;i++) f[i]=i,Ans[i]=1;
	sort(e+1,e+1+tot,cmp);
	for(register int i=1;i<=tot;i++)
	{
		int fx=find(e[i].from),fy=find(e[i].to);
		if(fx==fy) continue;
		f[fy]=fx;
		Ans[fx]=(Ans[fx]+e[i].h-h[fx])*(Ans[fy]+e[i].h-h[fy])%mod;
		h[fx]=e[i].h;
	}
	int fx=find(1);
	printf("%lld",(Ans[fx]+H-h[fx]+mod)%mod);
}

你可能感兴趣的:(并查集,最小生成树,P5852,POI2018,水箱)