大致就是有一个01矩阵,保证任意两个1之间只有一条路径相连,相邻两个1视作连边
每次询问一个区间内有多少个联通块,1e5个询问,n和m是2000以内
可以发现图必定是一棵树,那么联通块个数就是点数-边数
维护一下前缀边数和点数,在处理一下一条线上的连边数量,就可以O(1)得到答案了
代码如下
#include<bits/stdc++.h>
#pragma GCC optimize(2)
#define RG register
using namespace std;
int ans,tot,dp[2005][2005],sum[2005][2005],a[2005][2005],n,m,q;
int f[2005][2005],another[2005][2005];
int all,shu1,shu2,shu3,shu4;
char s[2005];
int dx[5]={0,1,0};
int dy[5]={0,0,1};
int read()
{
char c;
int x;
for(c=getchar();(c>'9'||c<'0')&&c!='-';c=getchar());
if(c=='-')
{
x=0;
for(c=getchar();c>='0'&&c<='9';c=getchar())
x=x*10+c-'0';
return -x;
}
else
{
x=c-'0';
for(c=getchar();c>='0'&&c<='9';c=getchar())
x=x*10+c-'0';
return x;
}
}
int main()
{
freopen("duty.in","r",stdin);
freopen("duty.out","w",stdout);
cin>>n>>m>>q;
for(RG int i=1;i<=n;i++)
{
scanf("%s",s+1);
for(RG int j=1;j<=m;j++)
{
if(s[j]=='1')
a[i][j]=1;
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
sum[i][j]=sum[i][j-1]+sum[i-1][j]-sum[i-1][j-1]+a[i][j];
}
}
for(int i=2;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
dp[i][j]=dp[i][j-1]+(a[i][j]==a[i-1][j]&&a[i][j]==1);
}
}
for(int j=2;j<=m;j++)
{
for(int i=1;i<=n;i++)
{
f[i][j]=f[i-1][j]+(a[i][j]==a[i][j-1]&&a[i][j]==1);
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
another[i][j]=another[i][j-1]+another[i-1][j]-another[i-1][j-1]+(a[i][j]==1&&a[i][j]==a[i][j-1])+(a[i][j]==1&&a[i][j]==a[i-1][j]);
}
}
for(RG int i=1;i<=q;i++)
{
shu1=read();
shu2=read();
shu3=read();
shu4=read();
int point=sum[shu3][shu4]-sum[shu1-1][shu4]-sum[shu3][shu2-1]+sum[shu1-1][shu2-1];
int edge=another[shu3][shu4]+another[shu1-1][shu2-1]-another[shu1-1][shu4]-another[shu3][shu2-1];
edge-=f[shu3][shu2]-f[shu1-1][shu2]+dp[shu1][shu4]-dp[shu1][shu2-1];
printf("%d\n",point-edge);
}
return 0;
}
/*
3 4 1
1101
0110
1101
2 2 3 4
*/