P5952 [POI2018] 水箱

发布时间 2023-08-09 08:28:26作者: tx_infinity

原题链接\

题目大意

\(有一个高度为H的水箱,里面有n*m个格子,每两个相邻的格子之间有一个隔板,问水位总共有多少中可能的情况,对10^9+7取模\)

数据范围

\(1\le n*m\le 500000,1\le H\le 10^9\)
\(先考虑只有两个格子一个隔板的情况,设此隔板高度为a,则在两边都不溢出时,共(a+1)^2种方案\)
\(溢出之后会变为一个格子,此时两个格子的水位一定相同\)
\(考虑a+1是怎么得来的,显然在没溢出时,每个格子水位可以是0,1,2,.....a-1,a,共a+1种情况\)
\(设f_i表示第i个格子所在的连通块合成前(即没有彻底联通)有多少种水位\)
\(h_i表示第i个格子所在的联通块最终联通时要越过的隔板的高度\)
\(则当i,j之间有高为k的隔板时把i和j合并,f_i=f_j=(f_i+k-h_i)*(f_j+k-h_j)\)
\(考虑合并顺序,可以发现进行了合并操作的隔板一定有n*m-1块,且一定在原图的最小生成树上\)
\(于是可以把原图的隔板看作边,跑最小生成树,并同时更新答案\)
\(代码\)

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=1e9+7;
int n,m,H;
int jntm(int x,int y)
{
	return x*m+y;
}
struct bian
{
	int l,r,v;
	friend bool operator<(bian i,bian j)
	{
		return i.v<j.v;
	}
}b[5000009];
int top,f[5000009],h[5000009],ans[5000009];
int find(int x)
{
	if(f[x]==x)
	return x;
	return f[x]=find(f[x]);
}
signed main()
{
	cin>>n>>m>>H;
	for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++)
	{
		f[jntm(i,j)]=jntm(i,j);
		ans[jntm(i,j)]=1;
	}
	for(int i=1;i<=n;i++)
	for(int j=1;j<m;j++)
	{
		int x;
		cin>>x;
		b[++top]={jntm(i,j),jntm(i,j+1),x};
	}
	for(int i=1;i<n;i++)
	for(int j=1;j<=m;j++)
	{
		int x;
		cin>>x;
		b[++top]={jntm(i,j),jntm(i+1,j),x};
	}
	sort(b+1,b+top+1);
	for(int i=1;i<=top;i++)
	{
		int l=b[i].l,r=b[i].r;
		if(find(l)==find(r))
		continue;
		l=find(l);
		r=find(r);
		ans[l]=(ans[l]+b[i].v-h[l])*(ans[r]+b[i].v-h[r])%mod;
		h[l]=b[i].v;
		f[r]=l;
	}
	int ngm=(ans[find(jntm(1,1))]+H-h[find(jntm(1,1))])%mod;
	cout<<(ngm%mod+mod)%mod;
	return 0;
}