「USACO 2021.1 Platinum」Paint by Letters

「USACO 2021.1 Platinum」Paint by Letters

统计连通块问题,暴力是\(O(qn^2)\),而且常数大

容易想到 平面图的欧拉定理 优化

下文和代码中,\(V,E,F\)分别为点集,边集,区域集合

其中\(|V|\)可以直接得到,\(|E|\)可以\(O(n^2)\)前缀和预处理出来,\(O(1)\)查询

下面处理区域个数

Solution1

前缀和所有大小为1(即被四个点包住的)区域,暴力预处理所有大小\(>1\)的区域,个数为\(O(\frac{nm}{4})\)

然后可以转化为一个对于给定矩形查询包含的 矩形个数 的问题

实际上这个题目\(q\)小直接枚举就是了。。

复杂度为\(O(nm+q\frac{nm}{4})\),足够通过时限

写的够丑可以得到这个代码

矩形区间查询问题 ,不知道有没有什么更好的方法

ps:垃圾数据没有卡,因此实际上数据中的空白块数量非常少,预处理写得稍微好一点可能还比下面的做法常数小

\[\ \]

Solution2

\((x,y)\)表示\((x,y),(x+1,y),(x,y+1),(x+1,y+1)\)中间的一个空白区域

这些空白块会被染色块之间的边隔开,但是依然可以形成四联通块

预处理出所有空白区域的连通块,每个连通块选取一个代表点\(S_i\)

我们要统计一个区域中的空白连通块个数,注意到

跨出区域范围的空白点,并不是断开了,而是和最外层的无穷空白区合并在一起

因此可以先求出在区域中存在的连通块个数,然后将连通到区域外的部分去掉

具体实现上:

前缀和预处理出\(S_i\)的位置,每次查询区域中的\(S_i\)个数(这样的统计不完全)

然后将\(S_i\)在区域中,且跨出区域的白色连通块删掉即可

跨出部分枚举四条边界即可

每次查询枚举边界,因此复杂度为\(O(nm+q(n+m))\)

#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(){
	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);
	}
}
posted @ 2021-03-02 13:00  chasedeath  阅读(204)  评论(0编辑  收藏  举报