GMOJ 6860. 【2020.11.14提高组模拟】鬼渊传说(village)

题解

这题考场打了60分,没想到80分。

这题我从听完题思考了一下细节就开始打,除掉5:00到7:00的洗澡+吃饭+腐败,一直打到晚上9:00才切。

感谢出题人,对我的心态锻炼起到了很大的作用。

不过yysy,这题打完的收获还是挺大的。

60分悬线法,很裸。

80分的做法要判断联通性,可以考虑用欧拉公式:

以下内容摘自OI wiki

对于任意的连通的平面图 \(G\),有:

\[n+r-m=2 \]

其中,\(n,m,r\)分别为\(G\)的阶数(顶点数),边数,面数。

而对于这道题而言,面数\(r\)显然就是四点环的个数+1,如果一个子矩阵有且仅有一个连通块,显然当且仅当他满足欧拉公式。

证明很简单,在此省略。

具体做法就是对于预处理出顶点数,边数,四点环数的前缀和,这个东西比较ex,反正我是处理了7个前缀和来维护。

主要是这个东西不能像普通的前缀和一样直接做差,要判断相邻的点之间的影响。

然后就可以枚举上,下,右边界,然后用一个桶来维护个数,在里面统计答案。

然后对于满分算法,也就是处理空腔:

  • 考虑将所有空腔预处理出来,找到包含每个空腔的最小子矩阵。
  • 然后将这些空腔以子矩阵的下边界排序。
  • 每次枚举到上边界时,将这个边界下方的空腔统计出来。
  • 然后用一个指针,每次将当前枚举到的范围内的空腔找出来,以此来确定左边界的范围。
  • 再用一个指针维护桶就行了。

思路不难,但是知识点不会,细节非常之多。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 500
using namespace std;
int n,m,i,j,k,ans,bkt[N*N*10],bz[N][N],map[N][N],num,x0,y0,x1,y1,fx[5][3],lim[N],p,data[N*N],tot,q;
int sumh[N][N],suml[N][N],sumh1[N][N],suml1[N][N];
char ch[N];
struct empty{
	int x0,y0,x1,y1;
}fp[N*N];
struct sumf{
	int node,edge,mian;
}sum[N][N];
void dfs(int x,int y){
	int i,xx,yy;
	bz[x][y]=1;
	x0=min(x,x0);y0=min(y,y0);
	x1=max(x,x1);y1=max(y,y1);
	for (i=1;i<=4;i++){
		xx=x+fx[i][1];yy=y+fx[i][2];
		if (bz[xx][yy]==0&&map[xx][yy]==0&&xx>0&&yy>0&&xx<=n&&yy<=m)
			dfs(xx,yy);
	}
}
int cmp(empty x,empty y){
	return x.x1<y.x1;
}
int solve(int l,int r,int k){
	int s1=sum[r][k].node-sum[l][k].node;
	int s2=sum[r][k].edge-sum[l][k].edge-sumh[l+1][k];
	int s3=sum[r][k].mian-sum[l][k].mian-sumh1[l+1][k];
	return s1+s3-s2;
}
int main(){
	freopen("village.in","r",stdin);
	freopen("village.out","w",stdout);
	fx[1][1]=1;fx[1][2]=0;
	fx[2][1]=-1;fx[2][2]=0;
	fx[3][1]=0;fx[3][2]=1;
	fx[4][1]=0;fx[4][2]=-1;
	scanf("%d%d\n",&n,&m);
	for (i=1;i<=n;i++){
		scanf("%s\n",ch+1);
		for (j=1;j<=m;j++) map[i][j]=ch[j]-'0';
	}
	for (i=1;i<=n;i++)
		for (j=1;j<=m;j++)
			if (bz[i][j]==0&&map[i][j]==0){
				x0=y0=1e9;x1=y1=0;
				dfs(i,j);
				if (x0>1&&y0>1&&x1<n&&y1<m)
					fp[++num]=(empty){x0,y0,x1,y1};
			}
	for (i=1;i<=n;i++)
		for (j=1;j<=m;j++){
			sum[i][j].node=sum[i-1][j].node+sum[i][j-1].node-sum[i-1][j-1].node+(map[i][j]==1);
			sum[i][j].edge=sum[i-1][j].edge+sum[i][j-1].edge-sum[i-1][j-1].edge+(map[i][j]==1&&map[i-1][j]==1)+(map[i][j]==1&&map[i][j-1]==1);
			sum[i][j].mian=sum[i-1][j].mian+sum[i][j-1].mian-sum[i-1][j-1].mian+(map[i][j]==1&&map[i-1][j]==1&&map[i][j-1]==1&&map[i-1][j-1]==1);
			sumh[i][j]=sumh[i][j-1]+(map[i][j]==1&&map[i-1][j]==1);
			suml[i][j]=suml[i-1][j]+(map[i][j]==1&&map[i][j+1]==1);
			sumh1[i][j]=sumh1[i][j-1]+(map[i][j]==1&&map[i-1][j]==1&&map[i][j-1]==1&&map[i-1][j-1]==1);
			suml1[i][j]=suml1[i-1][j]+(map[i][j]==1&&map[i-1][j]==1&&map[i][j+1]==1&&map[i-1][j+1]==1);
		}
	sort(fp+1,fp+num+1,cmp);
	for (i=1;i<=n;i++){
		tot=0;
		for (j=1;j<=num;j++)
			if (i<fp[j].x0) data[++tot]=j;
		for (j=1;j<=m;j++) lim[j]=0;
		p=0;
		for (j=i;j<=n;j++){
			while (j>fp[data[p+1]].x1&&p+1<=tot) lim[fp[data[p+1]].y1+1]=max(lim[fp[data[p+1]].y1+1],fp[data[p+1]].y0-1),p++;
			q=0;
			if (i==1&&j==3){
				int lsp=0;
			}
			for (k=1;k<=m;k++){
				k--;
				bkt[solve(i-1,j,k)-(suml[j][k]-suml[i-1][k])+(suml1[j][k]-suml1[i-1][k]-(map[i][k]==1&&map[i][k+1]==1&&map[i-1][k]==1&&map[i-1][k+1]==1))+1+n*n]++;
				k++;
				while (q+1<=lim[k]&&q<=m) bkt[solve(i-1,j,q)-(suml[j][q]-suml[i-1][q])+(suml1[j][q]-suml1[i-1][q]-(map[i][q]==1&&map[i][q+1]==1&&map[i-1][q]==1&&map[i-1][q+1]==1))+1+n*n]--,q++;
				ans+=bkt[solve(i-1,j,k)+n*n];
			}
			q=0;
			for (k=1;k<=m;k++){
				k--;
				bkt[solve(i-1,j,k)-(suml[j][k]-suml[i-1][k])+(suml1[j][k]-suml1[i-1][k]-(map[i][k]==1&&map[i][k+1]==1&&map[i-1][k]==1&&map[i-1][k+1]==1))+1+n*n]--;
				k++;
				while (q+1<=lim[k]&&q<=m) bkt[solve(i-1,j,q)-(suml[j][q]-suml[i-1][q])+(suml1[j][q]-suml1[i-1][q]-(map[i][q]==1&&map[i][q+1]==1&&map[i-1][q]==1&&map[i-1][q+1]==1))+1+n*n]++,q++;
			}
		}
	}
	printf("%d\n",ans);
	return 0;
}
posted @ 2020-11-14 21:44  Mohogany  阅读(301)  评论(0编辑  收藏  举报