6860. 【2020.11.14提高组模拟】鬼渊传说

63 篇文章 0 订阅
19 篇文章 0 订阅

Description

雏见泽是一个和平宁静的小村庄,但是在许多年前这里并不如现在那么和平。从沼泽中涌出的袭击人的鬼,以及流着“鬼之血”的村人们,使得其有了“鬼渊村”的称号。尽管多年过去,鬼渊村也称为了人们口中的传说,但种种线索表明鬼渊村的传说并非空穴来风,并且现在的雏见泽很可能保留着鬼渊村的部分习俗。因此,梨花打算出发寻找鬼渊村旧址,以此来追寻失落的真相。雏见泽可以看作一个 n ∗ m 的网格图,其中有若干位置可能是曾经鬼渊村的住户,在网格图上用黑色来标明,其余的格子用白色表示。由于雏见泽是鬼渊村扩建形成的,所以鬼渊村是雏见泽中一块矩形区域,并且显然要有至少一户人家。经过梨花的进一步调查,在当时由于鬼渊的传说,使得在村外的人对其极为恐惧和排斥。因此鬼渊村的村民为了自保全村上下团结一致,连住房都紧密相连,所以只有一个由房屋组成的四连通块。同时为了充分利用空间,不存在若干房屋把一个空地围起来的情况,即连通块内部不存在空地。现在梨花搞到了雏见泽的地图,在出发之前她想知道有多少个地方可能是鬼渊村旧址。
简要题意:
给出一个网格图,每个格子有黑白二色,求有多少个子矩形满足将其挖出来后恰好有一个黑色四连通块且不存在由白色格子组成的空腔
空腔:某个白色格子在空腔内当且仅当其不能通过上下左右四方向走到边界

Input

第一行两个数 n , m ,表示网格图的大小
接下来 n 行每行一个长为 m 01 串,表示网格图的颜色情况( 0 为白 1 为黑)

Output

一行一个数表示答案

Sample Input

4 4
1111
1101
1001
1111

Sample Output

83
【样例 2/3 输入】
见下发文件 ex_village2/3/4.in
【样例 2/3 输出】
见下发文件 ex_village2/3/4.out

Data Constraint

对于 100 % 数据满足 0 < n, m 300
数据点编号       ≤         m                 特殊性质
1,2                   5           5
3,4                  20         20
5,6                  300       300         保证没有两个黑色格子相邻
7,8                  300       300         保证任意子矩形不存在空腔
9,10                300       300

Solution

因nm同阶所以以下把m当成n

subtask1,2

并不知道怎么做

subtask3,4

枚举四边界再暴力bfs找连通块,时间复杂度O(n^6)

subtask5,6

悬线法

发现只要有大于一个黑色格子就不合法,所以枚举上下右边界,左边界范围可以算出来 时间复杂度 O(n^3)

subtask7,8

考虑欧拉公式n+r=m+2,由于不存在空腔所以r=四元环个数+1,因此有 n+四元环个数-m=1

把点,横边,竖边,四元环找出来前缀和,枚举上下右边界,左边界用桶直接求 时间复杂度 O(n^3)

这里注意:

前缀和统计答案的时候我们统计,当前(上下右边界前面所有的n+四元环个数-m)减去桶中前面某一个右端点结束的前缀和=1的个数

【也就是查询桶中当前(上下右边界前面所有的n+四元环个数-m)-1的个数。】

但是我们发现这样统计的并不是严格的某个矩形中的n+r-m的值,因为左端点与左端点前面那一列的四元环个数以及横边的个数没有减去

因此用桶存的时候 ,要存 值为【(  上下右边界前面所有的n+四元环个数-m的值)+(当前右边界与右边一列的四元环个数以及横边的个数)】的个数 。

subtask9,10

发现如果一个子矩形里面存在空腔,则将其四边界扩展后空腔仍存在

bfs/dfs找出所有空腔,总数上限为n^2 ,在固定了上下右边界后,每个空腔是对左边界的限制

把空腔所在的子矩形(x1,y1,x2,y2)找出来,在上边界<=x1-1时在(x2+1,y2+1)处打上y1-1的限制,之后枚 举下右边界时再处理出c[i]表示当前右边界为i的最大左边界,指针+桶单调维护左边界

时间复杂度O(n^3)

为什么倒数第二个数据点本地跑答案总是错的交上去就A了?T^T

 

Code

#include<cstdio> 
#include<cstring>
#include<algorithm>
#define I int
#define F(i,a,b) for(register I i=a;i<=b;i++)
#define N 303
using namespace std;
I n,m,a[N][N],s[N][N],dt[N],t[N*N+300],e[N][N],E[N][N],c[N][N],bz[N][N],x1,x2,y1,y2,l,ans,tot;
struct node{I x1,y1,x2,y2;}v[N*N];
char ch;
I R(I &x){
	x=0;I w=1;ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-') w=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*=w;
}
void dg(I x,I y){
	if(x<=0||y<=0||x>n||y>m||a[x][y]||bz[x][y]) return;
	bz[x][y]=1;
	x1=min(x1,x),x2=max(x2,x),y1=min(y1,y),y2=max(y2,y);
	dg(x-1,y),dg(x+1,y),dg(x,y-1),dg(x,y+1);
}
I cmp(node a,node b){return a.x1>b.x1;}
I main(){
	freopen("village.in","r",stdin);
	freopen("village.out","w",stdout);
	R(n),R(m);
	F(i,1,n){
		F(j,1,m){
			ch=getchar();
			while(ch!='0'&&ch!='1') ch=getchar();
			a[i][j]=ch=='1';
			s[i][j]=s[i-1][j]+a[i][j];
		}
	}
	F(i,1,n){
		F(j,1,m) if(!a[i][j]&&!bz[i][j]){
			dg(x1=x2=i,y1=y2=j);
			if(--x1&&--y1&&++x2<=n&&++y2<=m) v[++tot]=node{x1,y1,x2,y2};
		}
	}
	sort(v+1,v+1+tot,cmp);
	F(i,1,n){
		F(j,1,m){
			e[i][j]=e[i-1][j]+(a[i][j]&a[i-1][j]);
			E[i][j]=E[i-1][j]+(a[i][j]&a[i][j-1]);
			c[i][j]=c[i-1][j]+(a[i][j]&a[i-1][j]&a[i][j-1]&a[i-1][j-1]);
		}
	}
	F(i,1,n){
		F(j,i,n) F(k,1,m) bz[j][k]=0;
		F(j,1,tot){
			if(i<=v[j].x1) bz[v[j].x2][v[j].y2]=max(bz[v[j].x2][v[j].y2],v[j].y1);
			else break;
		}
		F(j,i,n){
			l=0;t[0]=1;//注意边界情况,l从0开始删,调了好久
			F(k,1,m){
				bz[j][k]=max(bz[j][k],bz[j-1][k]);
				while(l<bz[j][k]) t[dt[l]+c[j][l+1]-c[i][l+1]-E[j][l+1]+E[i-1][l+1]]--,l++;
				dt[k]=dt[k-1]+s[j][k]-s[i-1][k]+c[j][k]-c[i][k]-e[j][k]+e[i][k]-E[j][k]+E[i-1][k];
				ans+=t[dt[k]-1];
				t[dt[k]+c[j][k+1]-c[i][k+1]-E[j][k+1]+E[i-1][k+1]]++;
			}
			F(k,0,m) t[dt[k]+c[j][k+1]-c[i][k+1]-E[j][k+1]+E[i-1][k+1]]=0,dt[k]=0;
		}
	}
	printf("%d\n",ans);
	return 0;
}

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值