大致就是有一个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
*/