题解
这题考场打了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,&m);
for (i=1;i<=n;i++){
scanf("%s
",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
",ans);
return 0;
}