牛客多校第九场J-The Escape Plan of Groundhog(桶)(前缀和)

本文探讨了一个在特定矩阵大小限制下(n∈[1,500])的问题,通过使用前缀和与桶排序策略,有效地解决了矩阵中寻找满足特定条件的子矩阵的问题。文章详细介绍了如何利用前缀和维护矩阵的上下边缘,并通过桶排序来统计满足01元素差不超过1的条件的子矩阵数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这题卡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=ji+1表示当前列都是1
  • 再看内部01差不超过1的条件
  • 我们把0变成-1
  • sks_ksk为到kkk列为止iii行和jjj行中间的前缀和
  • 用一个桶维护每个sks_ksk出现次数
  • ∣sk1−sk2∣≤1|s_{k1}-s_{k2}|\leq1sk1sk21即可表示内部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);		
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值