Loading

P5930 [POI1999]降水

P5930 [POI1999]降水

虽然是1999年的题了,但是还挺值得一做,因为它使我发现我完全不会并查集,想了好久,甚至这种1k的代码都写挂了

这题目如果值域再大估计就要搜索了,然而在tg混久了发现不会搜索了,但是值域只有 \(10000\)

这使我们容易联想到维护每个高度上的横截面面积,然后从小到大扫一遍每个截面

考虑用并查集维护连通性(同时维护每个集合的大小)

\(0\) 下标表示与外界联通,即不能放水。那么 \(siz[find(0)]\) 就是这个截面非土地部分不能放水的面积

开个变量统计一下当前截面非土地部分面积即可

目前是洛谷最优解(要开O2),话说是不是我因为维护了集合大小然后自己胡了个启发式合并+路径压缩的并查集上去qwq

另外,我怀疑这五个点是一样的,因为我WA过,RE过,AC过,五个点的状态一样,运行时间大致相同,WA的时候返回的详细信息都相同

时间复杂度 \(O(V+nm\alpha)\)

#include<bits/stdc++.h>
using namespace std;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=0;ch=getchar();}
	while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
	return f?x:-x;
}
#define N 105
int n,m,a[N][N],now,ans,H;
int F[N*N],siz[N*N];
#define id(x,y) ((x-1)*m+y)
vector<pair<int,int> >v[10005];
#define pb push_back
#define x first
#define y second
#define mkp make_pair
int find(int x){return x==F[x]?x:F[x]=find(F[x]);}
void merge(int x,int y){
	x=find(x),y=find(y);
	if(x==y)return;
	if(siz[x]<siz[y])F[x]=y,siz[y]+=siz[x];
	else F[y]=x,siz[x]+=siz[y];
}
signed main(){
	n=read(),m=read();
	for(int i=1;i<=n;++i)
		for(int j=1;j<=m;++j)
			v[a[i][j]=read()].pb(mkp(i,j)),H=max(H,a[i][j]);
	for(int i=1,mx=n*m;i<=mx;++i)F[i]=i,siz[i]=1;
	for(int i=1;i<=H;++i){
		for(pair<int,int> j:v[i]){
			int x=j.x,y=j.y;++now;
			if(x==1||x==n||y==1||y==m)merge(id(x,y),0);
			if(y>1&&a[x][y-1]<=a[x][y])merge(id(x,y),id(x,y-1));
			if(x>1&&a[x-1][y]<=a[x][y])merge(id(x,y),id(x-1,y));
			if(y<m&&a[x][y+1]<=a[x][y])merge(id(x,y),id(x,y+1));
			if(x<n&&a[x+1][y]<=a[x][y])merge(id(x+1,y),id(x,y));
		}
		ans+=now-siz[find(0)];
	}
	printf("%d\n",ans);
	return 0;
}
posted @ 2020-10-24 15:46  zzctommy  阅读(81)  评论(0编辑  收藏  举报