真思维题。
考虑随便用一个特殊点来表示整个联通块,那么有:
1. 若该联通块未被包含,那么特殊点未被包含。
2. 若该联通块被完全包含,那么特殊点一定被包含。
3. 部分包含不能确定。
用二维前缀和维护1,2。部分包含的联通块一定出现在矩形边界上,把边界搞出来再把为被统计的统计进去即可。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#define fs first
#define sc second
using namespace std;
int n,m,q,numc,c[2010][2010],s[2010][2010],gx[4]={0,0,1,-1},gy[4]={1,-1,0,0};
char a[2010][2010];
bool vis[4000010];
pair<int,int> f[4000010];
int read()
{
int x=0;char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar());
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
return x;
}
void dfs(int x,int y)
{
c[x][y]=numc;
for(int i=0;i<=3;i++)
{
int xx=x+gx[i],yy=y+gy[i];
if(c[xx][yy]||a[x][y]!=a[xx][yy]||xx<=0||xx>n||yy<=0||yy>m) continue;
dfs(xx,yy);
}
}
bool pd(int c,int x1,int y1,int x2,int y2)
{
return f[c].fs<=x2&&f[c].sc<=y2&&f[c].fs>=x1&&f[c].sc>=y1;
}
int main()
{
n=read();m=read();
for(int i=1;i<=n;i++)
scanf("%s",a[i]+1);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(!c[i][j])
{
numc++;
f[numc]=make_pair(i,j);
s[i][j]=1;
dfs(i,j);
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
s[i][j]+=s[i-1][j]+s[i][j-1]-s[i-1][j-1];
q=read();
while(q--)
{
int x1=read(),y1=read(),x2=read(),y2=read(),ans;
ans=s[x2][y2]-s[x1-1][y2]-s[x2][y1-1]+s[x1-1][y1-1];
for(int i=x1;i<=x2;i++)
{
if(!vis[c[i][y1]]&&(!pd(c[i][y1],x1,y1,x2,y2))) vis[c[i][y1]]=1,ans++;
if(!vis[c[i][y2]]&&(!pd(c[i][y2],x1,y1,x2,y2))) vis[c[i][y2]]=1,ans++;
}
for(int i=y1;i<=y2;i++)
{
if(!vis[c[x1][i]]&&(!pd(c[x1][i],x1,y1,x2,y2))) vis[c[x1][i]]=1,ans++;
if(!vis[c[x2][i]]&&(!pd(c[x2][i],x1,y1,x2,y2))) vis[c[x2][i]]=1,ans++;
}
printf("%d\n",ans);
for(int i=x1;i<=x2;i++)
vis[c[i][y1]]=vis[c[i][y2]]=0;
for(int i=y1;i<=y2;i++)
vis[c[x1][i]]=vis[c[x2][i]]=0;
}
return 0;
}