题目链接
考虑枚举降雨量,把一开始的每个点的高度记录在一起,然后根据降雨量枚举。
往当前<=H的点施加降水,然后维护会流出去多少水,ans更新。
枚举一个点check四周比它低的点,合并到一起。把溢出的点连向虚拟的0,因此每次合并都是把溢出的路线合并到0以下。
#include <bits/stdc++.h>
using namespace std;
#define N 1010
#define V 1000009
#define int long long
int mx[4]={0,0,1,-1};
int my[4]={1,-1,0,0};
namespace Union_Find{
int fa[V],siz[V];
void init(int n){
fa[0]=siz[0]=0;
for(int i=1;i<=n;i++)fa[i]=i,siz[i]=1;
}
int find(int x){
if(fa[x]==x)return x;
return fa[x]=find(fa[x]);
}
void merge(int x,int y){
int fx=find(x),fy=find(y);
if(fx==fy)return;
siz[fy]+=siz[fx];
fa[fx]=fy;
}
};
int n,m;
bool vis[N][N];
vector<pair<int,int> > g[V];
bool checkin(int x,int y){
if(x<1||y<1||x>n||y>m)return 0;
return 1;
}
int id(int x,int y){
if(!checkin(x,y))return 0;
return (x-1)*m+y;
}
int cnt=0,ans=0;
signed main(){
using namespace Union_Find;
// freopen("2936.in","r",stdin);
scanf("%lld",&n);
m=n;
init(n*m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
int vl;scanf("%lld",&vl);
g[vl].push_back(pair<int,int>(i,j));
}
int up=n*m;
for(int v=0;v<=V;v++){//考虑一层一层地加水
if(siz[find(0)]==up)break;
for(int j=0;j<g[v].size();j++){
int x=g[v][j].first,y=g[v][j].second;
vis[x][y]=1;
cnt++;
for(int k=0;k<4;k++){
int nx=x+mx[k],ny=y+my[k];
//如果当前点在边界上或者旁边有比它低的点就合并起来
//在边界上siz[0]++ 说明这个点不可取
//不在边界上维护可以到达的联通块
if(!checkin(nx,ny)||vis[nx][ny])merge(id(nx,ny),id(x,y));
}
}
ans+=cnt-siz[find(0)];
}
printf("%lld",ans);
return 0;
}