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;
}
路漫漫其修远兮,吾将上下而求索