这题卡logN复杂度就离谱
Description
Solution
- 首先发现n∈[1,500]n \in[1,500]n∈[1,500]的数据
不要看成1500料想复杂度O(N3)O(N^3)O(N3) - 我们枚举上下边缘,再一起扫过去
- 我们考虑四边都为1的条件
- 上下可以轻松维护,对左右,我们记dijd_{ij}dij为第jjj列从上到下的前缀和
- dij=j−i+1d_{ij}=j-i+1dij=j−i+1表示当前列都是1
- 再看内部01差不超过1的条件
- 我们把0变成-1
- 记sks_ksk为到kkk列为止iii行和jjj行中间的前缀和
- 用一个桶维护每个sks_ksk出现次数
- ∣sk1−sk2∣≤1|s_{k1}-s_{k2}|\leq1∣sk1−sk2∣≤1即可表示内部01差不超过1,统计进答案里
#include <cstdio>
const int N=510;
int n,m,ans,a[N][N],d[N][N],s[N],c[N*N];//c是桶
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",a[i]+j),a[i][j]=!a[i][j]?-1:1,d[i][j]=d[i-1][j]+a[i][j];
for(int i=1;i<=n;i++)
for(int j=i+1,pre=1;j<=n;j++){//pre:之前清零过的位置
pre=1;
for(int k=1;k<=m;k++){
if(a[i][k]==-1||a[j][k]==-1){
for(int l=pre;l<=k;l++)if(d[j][l]-d[i-1][l]==j-i+1) --c[s[l]];
pre=k+1;continue;
}if(d[j][k]-d[i-1][k]==j-i+1) ans+=c[s[k-1]]+c[s[k-1]+1]+c[s[k-1]-1];
s[k]=s[k-1]+d[j-1][k]-d[i][k];
if(d[j][k]-d[i-1][k]==j-i+1) ++c[s[k]];
}for(int k=pre;k<=m;k++)if(d[j][k]-d[i-1][k]==j-i+1) --c[s[k]];
}
printf("%d\n",ans);
}
map不能用
#include <bits/stdc++.h>
using namespace std;
const int N=510;
const int offset=N*N;
int n,m,ans,a[N][N],b[N][N],c[N][N],s[N][N],num[N*N*2];
int main()
{
char ch;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf(" %c",&ch),
s[i][j]=ch=='1'?1:-1,
a[i][j]=s[i][j]+a[i][j-1],
b[i][j]=b[i-1][j]+s[i][j],
c[i][j]=c[i-1][j]+c[i][j-1]-c[i-1][j-1]+s[i][j];
for(int u=1;u<=n;u++)
for(int v=u+1;v<=n;v++)
{
int l=1,r=1;
while(l<=m-1)
{
r=l+1;
if(b[v][l]-b[u-1][l]==v-u+1)
{
//printf("%d %d %d\n",u,v,l);
int sum=c[v-1][l]-c[u][l];
num[sum+offset]++;
r=l+1;
while(r<=m&&s[u][r]==1&&s[v][r]==1)
{
if(b[v][r]-b[u][r]!=v-u){++r;continue;}
int rsum=c[v-1][r-1]-c[u][r-1];
ans+=num[rsum+offset]+num[rsum-1+offset]+num[rsum+1+offset];
int lsum=c[v-1][r]-c[u][r];
num[lsum+offset]++;
++r;
}
num[sum+offset]--;
r=l+1;
while(r<=m&&s[u][r]==1&&s[v][r]==1)
{
if(b[v][r]-b[u][r]!=v-u){++r;continue;}
int lsum=c[v-1][r]-c[u][r];
num[lsum+offset]--;
++r;
}
}
l=r;
}
}
printf("%d\n",ans);
}