【JZOJ 省选模拟】Valleys

题目

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); 	
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值