题目
Description
Bessie喜欢观光,而今天她正在寻找景色优美的山谷。
她感兴趣的是一个N×N的方阵,其中每个格子都有一个高度。所有在此正方形方阵之外的格子的高度可以被看作是无限大。
山谷指的是一块连续、不含洞的一块区域,并且每个相邻的包围该区域的格子都高于这块区域中的所有格子。
更形式化地说:
一组格子被称作是“沿边相邻的”,如果可以从其中任意一个格子出发,经过一些沿上、下、左、右方向的移动,到达其中所有其他格子。
一组格子被称作是“沿点相邻的”,如果可以从其中任意一个格子出发,经过一些沿上、下、左、右、对角线方向的移动,到达其中所有其他格子。
一个“区域”指的是一组非空并且沿边相邻的格子。
一个区域被称作是“有洞的”,如果这个区域的补集(包括在N×NN×N方阵之外的无限高格子)不是沿点相邻的。
区域的“边界”指的是所有与该区域内的某个格子正交相邻(上、下、左、右),但本身不在该区域内的格子。
一个“山谷”指的是某个非有洞区域,满足区域内的任意格子的高度低于该区域边界上任意格子的高度。
Bessie的目标是求出所有山谷的大小之和。
一些例子
这是一个区域:
oo.
ooo
…o
这不是一个区域(中间的格子和右下角的格子不沿边相邻):
oo.
oo.
…o
这是一个非有洞区域:
ooo
o…
o…
这是一个有洞的区域(“甜甜圈”中间的格子与区域外的格子不沿点相邻):
ooo
o.o
ooo
这是另一个非有洞区域(中间的格子与右下角的格子沿点相邻):
ooo
o.o
oo.
Input
输入的第一行包含N,其中1≤N≤750。
以下N行每行包含N个整数,为方阵每个格子的高度。所有高度hh满足1≤h≤10^6。所有高度均为不同的整数。
对于至少19%的测试数据,额外保证N≤100。
Output
输出一个整数,为所有山谷的大小之和。
Sample Input
3
1 10 2
20 100 30
3 11 50
Sample Output
30
在这个例子中,有三个大小为1的山谷:
o.o
…
o…
一个大小为2的山谷:
…
…
oo.
一个大小为3的山谷:
ooo
…
…一个大小为6的山谷:
ooo
o…
oo.
一个大小为7的山谷:
ooo
o.o
oo.
以及一个大小为9的山谷:
ooo
ooo
ooo
所以,答案为1 + 1 + 1 + 2 + 3 + 6 + 7 + 9 = 30。
Data Constraint
思路
若没有洞的限制,答案显然从每个点出发的极大联通块的大小和。(从高到低走)
那么将高度排序,从小到大用并查集维护即可。
现在要看有没有洞,考虑平面图欧拉公式:
V+F=E+2,其中V是点数,F是块数,E是边数,则F=E+2−V。
这里把每个格子定义为点,若相邻格子都属于当前并查集,则它们之间有边。
不难发现F=1(外面的大块)+相邻四个(小正方形)都属于当前并查集的个数+洞的个数
所以要动态地维护点数、边数、小正方形个数。
代码
#include<bits/stdc++.h>
using namespace std;
const int N=777,inf=1000000000;
int n,a[N][N];
int val(int i,int j)
{
return (n+2)*i+j;
}
int fa[N*N],rot[N*N],sz[N*N];
void init()
{
for(int i=0; i<760*760; i++) fa[i]=i,sz[i]=1;
}
int gf(int x)
{
return fa[x]==x?x:fa[x]=gf(fa[x]);
}
void merge(int i,int j)
{
i = gf(i), j = gf(j);
if(i!=j)
{
fa[i] = j;
sz[j] += sz[i];
rot[j] += rot[i];
}
}
int cid[760*760];
int Val[760*760];
bool cmp(int a,int b)
{
return Val[a]<Val[b];
}
int calc(int a,int b,int c)
{
if(a+b+c==0) return +1;
if(a+b+c==1)
{
if(b==1) return +1;
return -1;
}
if(a+b+c==2)
{
if(b==0) return -3;
return -1;
}
if(a+b+c==3) return +1;
}
int main()
{
freopen("valleys.in","r",stdin); freopen("valleys.out","w",stdout);
scanf("%d",&n);
for(int i=0; i<=n+1; i++)
for(int j=0; j<=n+1; j++)
a[i][j] = inf;
for(int i=1; i<=n; i++)
for(int j=1; j<=n; j++)
{
cin >> a[i][j];
}
for(int i=0; i<=n+1; i++)
for(int j=0; j<=n+1; j++)
{
cid[val(i,j)] = val(i,j);
Val[val(i,j)] = a[i][j];
}
sort(cid,cid+(n+2)*(n+2),cmp);
init();
long long ans = 0;
for(int m=0; m<(n+2)*(n+2); m++)
{
int cur = cid[m];
int i = cur/(n+2);
int j = cur%(n+2);
if(a[i][j]==inf) break;
if(a[i+1][j]<=a[i][j]) merge(val(i,j),val(i+1,j));
if(a[i-1][j]<=a[i][j]) merge(val(i,j),val(i-1,j));
if(a[i][j+1]<=a[i][j]) merge(val(i,j),val(i,j+1));
if(a[i][j-1]<=a[i][j]) merge(val(i,j),val(i,j-1));
int x = calc(gf(val(i-1,j))==gf(cur), gf(val(i-1,j-1))==gf(cur), gf(val(i,j-1))==gf(cur))
+ calc(gf(val(i,j-1))==gf(cur), gf(val(i+1,j-1))==gf(cur), gf(val(i+1,j))==gf(cur))
+ calc(gf(val(i+1,j))==gf(cur), gf(val(i+1,j+1))==gf(cur), gf(val(i,j+1))==gf(cur))
+ calc(gf(val(i,j+1))==gf(cur), gf(val(i-1,j+1))==gf(cur), gf(val(i-1,j))==gf(cur));
rot[gf(cur)] += x;
if(rot[gf(cur)] > 0) ans += sz[gf(cur)];
}
printf("%lld",ans);
}