「USACO 2021.1 Platinum」Paint by Letters
统计连通块问题,暴力是\(O(qn^2)\),而且常数大
容易想到 平面图的欧拉定理 优化
下文\(V,E,F\)分别为点集,边集,区域集合
其中\(|V|\)可以直接得到,\(|E|\)可以\(O(n^2)\)前缀和预处理出来,\(O(1)\)查询
下面处理区域个数
Solution1
前缀和所有大小为1(即被四个点包住的)区域,暴力预处理所有大小\(>1\)的区域,查询时枚举即可
复杂度为\(O(n^2+q\frac{n^2}{4})\),足够通过时限
Solution2
用\((x,y)\)表示\((x,y),(x+1,y),(x,y+1),(x+1,y+1)\)中间的一个空白区域
这些空白块会被染色块之间的边隔开,但是依然可以形成四联通块
预处理出所有空白区域的连通块,每个连通块选取一个代表点\(S_i\)
我们要统计一个区域中的空白连通块个数,注意到
跨出区域范围的空白点,并不是断开了,而是和最外层的无穷空白区合并在一起
因此可以先求出在区域中存在的连通块个数,然后将连通到区域外的部分去掉
具体实现上:
前缀和预处理出\(S_i\)的位置,每次查询区域中的\(S_i\)个数(这样的统计不完全)
然后将\(S_i\)在区域中,且跨出区域的白色连通块删掉即可
跨出部分枚举四条边界即可
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(int i=a,i##end=b;i>=i##end;--i)
const int N=1e3+10,INF=1e9+10;
const int dx[]={0,0,1,-1},dy[]={1,-1,0,0};
int n,m,q;
char A[N][N];
int vis[N*N],I[N][N],E[4][N][N],S[N][N];
// B,C预处理上/左边个数
int B[N][N],C[N][N];
int X[N*N],Y[N*N],cnt;
// 搜索空白连通块
void dfs(int x,int y){
I[x][y]=cnt;
rep(i,0,3) if(E[x][y][i]) {
int x1=x+dx[i],y1=y+dy[i];
if(!x1 || !y1||x1>=n || y1>=m || I[x1][y1]) continue;
dfs(x1,y1);
}
}
int Sum(const int A[N][N],int x1,int y1,int x2,int y2){
x1--,y1--;
return A[x2][y2]-A[x1][y2]-A[x2][y1]+A[x1][y1];
}
int main(){
freopen("letter.in","r",stdin),freopen("letter.out","w",stdout);
scanf("%d%d%d",&n,&m,&q);
rep(i,1,n) scanf("%s",A[i]+1);
rep(i,1,n) rep(j,1,m) {
E[0][i][j-1]=E[1][i][j]=A[i][j]!=A[i+1][j];
E[2][i-1][j]=E[3][i][j]=A[i][j]!=A[i][j+1];
// 处理一下空白点之间的边
if(A[i-1][j]==A[i][j]) B[i][j]++;
if(A[i][j]==A[i][j-1]) C[i][j]++;
}
// 预处理空白点之间的集合
rep(i,1,n-1) rep(j,1,m-1) if(!I[i][j]) {
X[++cnt]=i,Y[cnt]=j,vis[cnt]=q+1;
dfs(i,j),S[i][j]++;
}
rep(i,1,n) rep(j,1,m) {
B[i][j]+=B[i-1][j]+B[i][j-1]-B[i-1][j-1];
C[i][j]+=C[i-1][j]+C[i][j-1]-C[i-1][j-1];
S[i][j]+=S[i-1][j]+S[i][j-1]-S[i-1][j-1];
}
while(q--) {
int lx,ly,rx,ry; scanf("%d%d%d%d",&lx,&ly,&rx,&ry);
int V=(rx-lx+1)*(ry-ly+1);
int E=Sum(B,lx+1,ly,rx,ry)+Sum(C,lx,ly+1,rx,ry);
int F=Sum(S,lx,ly,rx-1,ry-1);
auto Check=[&](int i){ if(vis[i]!=q && lx<=X[i] && X[i]<rx && ly<=Y[i] && Y[i]<ry) vis[i]=q,F--; };
rep(i,lx,rx-1) {
if(::E[0][i][ry-1]) Check(I[i][ry-1]);
if(::E[1][i][ly]) Check(I[i][ly]);
}
rep(i,ly,ry-1) {
if(::E[2][rx-1][i]) Check(I[rx-1][i]);
if(::E[3][lx][i]) Check(I[lx][i]);
}
printf("%d\n",V-E+F);
}
}