题意简述:给定一个\(N*M(N,M<=2000)\)的矩阵,有黑白两种颜色,保证每个色块为一棵树。即如果块的大小为\(N\),那么块内公共边数为\(N-1\)。有\(q(q<=2*10^5)\)次询问,每次询问一个矩阵内有多少个联通块。
一开始搞矩阵内树的定义搞了好久。。
然后我们发现由于一棵树点数减去边数为1,那么我们只要记录点,边的二维前缀和,拿给定矩阵内的总点数减去总边数即为答案。
然后我敲了一发,发现样例都不能过。
debug了一手,发现我好想边的二维前缀和相减时,少减了边界上的。。。
然后想了一想,发现不大会处理。
于是去orz了一波yls博客,把行列分开记录就ok了。
这种题没自己做出来实在有点不应该。
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=2010;
int sum_v[N][N],sum_l[N][N],sum_r[N][N];
int n,m,q;
char s[N][N];
int read(){
char ch=getchar();int x=0,f=1;
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int main(){
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=n;i++){
scanf("%s",s[i]+1);
for(int j=1;j<=m;j++)
if(s[i][j]=='1'){
sum_v[i][j]=1;
if(s[i][j-1]=='1')sum_l[i][j]=1;
if(s[i-1][j]=='1')sum_r[i][j]=1;
}
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
sum_v[i][j]+=sum_v[i-1][j]+sum_v[i][j-1]-sum_v[i-1][j-1];
sum_l[i][j]+=sum_l[i-1][j]+sum_l[i][j-1]-sum_l[i-1][j-1];
sum_r[i][j]+=sum_r[i-1][j]+sum_r[i][j-1]-sum_r[i-1][j-1];
}
while(q--){
int x=read(),y=read(),xx=read(),yy=read();
int ans=sum_v[xx][yy]-sum_v[x-1][yy]-sum_v[xx][y-1]+sum_v[x-1][y-1];
ans-=sum_l[xx][yy]-sum_l[x-1][yy]-sum_l[xx][y]+sum_l[x-1][y];
ans-=sum_r[xx][yy]-sum_r[x][yy]-sum_r[xx][y-1]+sum_r[x][y-1];
printf("%d\n",ans);
}
}