[CSP-S模拟测试]:任(duty)(二维前缀和)

题目描述

$liu\_runda$退役之后就失去梦想开始咸鱼生活了……
$Bilibili$夏日画板活动中,所有人都可以在一块画板上进行像素画创作。$UOJ$群有一群无聊的人决定在画板上创作一个$50\times 50$的$UOJ$的$LOGO$。如下图:


这块画板实际上是很大的矩形网格。一个网格是一像素。
一个人每三分钟才能画一个像素。所以$liu\_runda$的咸鱼生活非常无聊。
郭神表示他实在是看不下去$liu\_runda$这只颓狗了,于是随手出了一道神题,$liu\_runda$不会做,于是给出到联考里了。
在画板上有一片黑白相间的矩形区域满足这样的性质:如果认为相同颜色的方块可以在上下左右四个方向连通,那么任意两个黑色方块要么不连通,要么连通但之间只有一条简单路径(不重复经过同一个格子的路径)。
这个矩形区域有$N$行$M$列,从上到下依次为第$1,2,3...N-1,N$行,从左到右依次为第$1,2,3...M-1,M$列。
每次郭神会询问这片矩形区域内的一个子矩形。在只考虑这个子矩形内的像素时(即从子矩形内部不能和子矩形之外的像素相连通),问这个子矩形内的黑色方块组成了多少连通块。
如果不能完成这个任务,$liu\_runda$就会被郭神批判一番……


输入格式

第一行三个整数$N$,$M$,$Q$,表示矩形区域有$N$行$M$列,有$Q$组询问。
接下来$N$行,每行一个长为$M$的$01$字符串。$0$表示白色,$1$表示黑色。第$i$行第$j$个字符表示第$i$行$j$列的颜色。
接下来$Q$行,每行$4$个整数$x_1,y_1,x_2,y_2,(x1<=x2,y1<=y2)$表示选出的矩形区域的两个对角。即选出一个左上角为第$x_1$行第$y_1$列,右下角为第$x_2$行第$y_2$列,包含$x_2-x_1+1$行,$y_2-y_1+1$列的区域。


输出格式

$Q$行,第$i$行一个整数$ans$表示第$i$组询问的答案。


样例

样例输入1:

3 4 4
1101
0110
1101
1 1 3 4
1 1 3 1
2 2 3 4
1 2 2 4

样例输出1:

3
2
2
2

样例输入2:

5 5 6
11010
01110
10101
11101
01010
1 1 5 5
1 2 4 5
2 3 3 4
3 3 3 3
3 1 3 5
1 1 3 4

样例输出2:

3
2
1
1
3
2


数据范围与提示

对于第$1,2$个测试点,$Q=1$。
对于第$3,4$个测试点,$N=1$。
对于第$5,6,7$个测试点,$N=2$。
对于第$8$个测试点,$N,M\leqslant 1,000$。
对于第$9$个测试点,$N,M\leqslant 1500$。
对于全部测试点,$1\leqslant N,M\leqslant 2,000,1\leqslant Q\leqslant 200,000,1\leqslant x_1\leqslant x_2\leqslant N,1\leqslant y_1\leqslant y_2\leqslant M$,保证任意两个黑色像素之间最多只有一条简单路径。


题解

题目应该不是很难懂,手膜一下样例就好了。

$70\%$算法:

暴力$DFS$看有几个联通块就好了。

时间复杂度:$\Theta(Q\times N\times M)$。

期望得分:$70$分。

实际得分:$70$分。

$100\%$算法:

看到$Q$这么大,前面的同学居然在想二维莫队,但是本蒟蒻并不会,所以我就想了前缀和。

显然前缀和不好搞,先来了解一个性质吧,在无环无向图中,联通块的个数=点数-边数。

注意是无环无向图,题目中恰巧给出了这一点,所以我们可以利用这条性质,去解决这道题。

那么,如何解决呢?

维护三个二维前缀和,二维前缀和想必大家都会,在此不再赘述。

就来说一下这三个前缀和都是什么吧:所有黑块的前缀和,横向连边的前缀和,纵向连边的前缀和。

有了这三个前缀和,我们就有了点数和边数,也就可以利用上面的那条性质了。

然而还有一点需要注意的就是边的前缀和的边界条件,就拿横向连边举例子,如下图中,我们需要计算蓝色区域联通块的个数:

那么我们在处理横向连边的时候需要注意红线位置的边,那些便也需要剪掉,方法就是在剪掉前缀和的时候将其$x$坐标右移一位即可。

时间复杂度:$\Theta(N\times M+Q)$。

期望得分:$100$分。

实际得分:$100$分。


代码时刻

#include<bits/stdc++.h>
using namespace std;
int n,m,q;
bool Map[3000][3000];
char ch[3000];
int rx,ry,cx,cy,ans;
int dp[3][3000][3000];
int main()
{
	scanf("%d%d%d",&n,&m,&q);
	for(int i=1;i<=n;i++)
	{
		scanf("%s",ch+1);
		for(int j=1;j<=m;j++)
			Map[i][j]=ch[j]-'0';
	}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
		{
			dp[0][i][j]=dp[0][i-1][j]+dp[0][i][j-1]-dp[0][i-1][j-1]+Map[i][j];
			dp[1][i][j]=dp[1][i-1][j]+dp[1][i][j-1]-dp[1][i-1][j-1];
			dp[2][i][j]=dp[2][i-1][j]+dp[2][i][j-1]-dp[2][i-1][j-1];
			if(Map[i][j]&&Map[i-1][j])dp[1][i][j]++;
			if(Map[i][j]&&Map[i][j-1])dp[2][i][j]++;
		}
	while(q--)
	{
		ans=0;
		scanf("%d%d%d%d",&rx,&ry,&cx,&cy);
		ans =dp[0][cx][cy]-dp[0][rx-1][cy]-dp[0][cx][ry-1]+dp[0][rx-1][ry-1];
		ans-=dp[1][cx][cy]-dp[1][rx  ][cy]-dp[1][cx][ry-1]+dp[1][rx  ][ry-1];
		ans-=dp[2][cx][cy]-dp[2][rx-1][cy]-dp[2][cx][ry  ]+dp[2][rx-1][ry  ];
		printf("%d\n",ans);
	}
	return 0;
}

rp++

转载于:https://www.cnblogs.com/wzc521/p/11354897.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值