图论+前缀和 任(duty)
问题 B: 任(duty)
时间限制: 2 Sec 内存限制: 512 MB
题目描述
liu_runda退役之后就失去梦想开始咸鱼生活了…
Bilibili夏日画板活动中,所有人都可以在一块画板上进行像素画创作.UOJ群有一群无聊的人决定在画板上创作一个50*50的UOJ的LOGO.如下图.
这块画板实际上是很大的矩形网格.一个网格是一像素.
一个人每三分钟才能画一个像素.所以liu_runda的咸鱼生活非常无聊.
郭神表示他实在是看不下去liu_rudna这只颓狗了,于是随手出了一道神题,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个整数x1,y1,x2,y2,(x1<=x2,y1<=y2)表示选出的矩形区域的两个对角.即选出一个左上角为第x1行第y1列,右下角为第x2行第y2列,包含x2-x1+1行,y2-y1+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<=1000
对于第9个测试点,N,M<=1500
对于全部测试点,1<=N,M<=2000,1<=Q<=200000,1<=x1<=x2<=N,1<=y1<=y2<=M,保证任意两个黑色像素之间最多只有一条简单路径.
其实题面里还有张很无聊的图,懒得粘了。。
因为保证联通块里不存在环,只能由一条边互相连通,而同一联通块满足点数-边数==1;所以求出区间里所有点和所有边,相减即为答案。
因此我们可以维护前缀和。但有些细节。
举个样例(为了方便说我把0,1改成了点的标号)
1 2 3 4
5 6 7 8
9 0 10 11
比如我们要求7,8,10,11的,那么要把3-7,4-8,6-7,0-10的边都剪掉,因此会多剪掉7-10和7-8,所以得额外维护单列的值,把它加回去,具体就不细说了。
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#define N 2005
#define inf 1000000000
using namespace std;
int read()
{
int sum=0,f=1;char x=getchar();
while(x<'0'||x>'9'){if(x=='-')f=-1;x=getchar();}
while(x>='0'&&x<='9'){sum=(sum<<1)+(sum<<3)+x-'0';x=getchar();}
return sum*f;
}
int n,m,q;
int a[2005][2005],dian[2005][2005],bian[2005][2005],h[2005][2005],z[2005][2005];
int l1,l2,r1,r2;
void init()
{
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
dian[i][j]=dian[i][j-1]+a[i][j];
bian[i][j]=bian[i][j-1];
if(a[i][j]==1&&a[i][j-1]==1)bian[i][j]++;
h[i][j]=bian[i][j];
}
for(int j=1;j<=m;j++)
for(int i=1;i<=n;i++)
{
dian[i][j]+=dian[i-1][j];
z[i][j]=z[i-1][j];
if(a[i][j]==1&&a[i-1][j]==1)z[i][j]++;
}
for(int i=1;i<=n;i++)
{
int s=0;
for(int j=1;j<=m;j++)
{
if(a[i][j]==1&&a[i-1][j]==1)s++;
bian[i][j]+=bian[i-1][j]+s;
}
}
/* for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
cout<<z[i][j]<<" ";
cout<<endl;
}*/
}
int main()
{
n=read();m=read();q=read();
char s[2005];
for(int i=1;i<=n;i++)
{
scanf("%s",s+1);
for(int j=1;j<=m;j++)
a[i][j]=s[j]-'0';
}
init();int ans2,ans1;
while(q--)
{
l1=read();r1=read();l2=read();r2=read();
ans1=dian[l2][r2]-dian[l2][r1-1]-dian[l1-1][r2]+dian[l1-1][r1-1];
ans2=bian[l2][r2]-bian[l2][r1]-bian[l1][r2]+bian[l1][r1];
ans2+=z[l2][r1]-z[l1][r1]+h[l1][r2]-h[l1][r1];
//cout<<ans1<<" "<<ans2<<endl;
printf("%d\n",ans1-ans2);
}
}