首页 > 其他分享 >Living-Dream 系列笔记 第13期

Living-Dream 系列笔记 第13期

时间:2024-03-09 12:35:52浏览次数:25  
标签:Living 13 int sum 枚举 cin 131 ans Dream

本期主要讲解二维前缀和。

知识点

我们令 \(a_{i,j}\) 表示原数组,则 \(sum_{i,j}\) 为 \(a\) 的二维前缀和数组。

根据容斥原理,得到递推式:

\[sum_{i,j}=sum_{i-1,j}+sum_{i,j-1}-sum_{i-1,j-1}+a_{i,j} \]

二维前缀和适用于求静态矩阵的子矩阵元素和。

若我需要求一个左上角坐标为 \((i,j)\)、右下角坐标为 \((x,y)\) 的子矩阵的元素和 \(S\),则再次根据容斥原理,得到公式:

\[S=sum_{x,y}-sum_{x,j-1}-sum_{i-1,y}+sum_{i-1,j-1} \]

例题

T1

\(O(n^4)\) 暴力枚举左上角和右下角,利用二维前缀和取元素和的 \(\max\) 即可。

#include<bits/stdc++.h>
#define int long long
using namespace std;

int n,ans=-1e18;
int a[131][131],sum[131][131];

signed main(){
	cin>>n;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			cin>>a[i][j],sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j];
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			for(int ii=i;ii<=n;ii++)
				for(int jj=j;jj<=n;jj++)
					ans=max(ans,sum[ii][jj]-sum[ii][j-1]-sum[i-1][jj]+sum[i-1][j-1]);
	cout<<ans;
	return 0;
}

T2

首先用二维前缀和计算出每个子矩阵的点的个数。

仍然 \(O(n^4)\) 枚举左上与右下,中间留出一个长和宽都比当前矩阵小 \(1\) 的矩阵,对所有这样的大矩阵中点的个数 \(-\) 小矩阵中点的个数取 \(\max\) 即可。

#include<bits/stdc++.h>
using namespace std;

int n,ans;
int a[131][131];
int sum[131][131];

int work(int x,int y,int i,int j){
	return sum[i][j]-sum[x-1][j]-sum[i][y-1]+sum[x-1][y-1];
}

int main(){
	cin>>n;
	for(int i=1,x,y;i<=n;i++) cin>>x>>y,a[x][y]++;
	for(int i=1;i<=100;i++)
		for(int j=1;j<=100;j++)
			sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j];
	for(int x=1;x<=100;x++)
		for(int y=1;y<=100;y++)
			for(int i=x+1;i<=100;i++)
				for(int j=y+1;j<=100;j++){
					int out=work(x,y,i,j),in=work(x+1,y+1,i-1,j-1);
					ans=max(ans,out-in);
				}
	cout<<ans;
    return 0; 
}

T3

倒序枚举边长,对于每一种边长枚举左上角坐标,利用二维前缀和求出其中 \(1\) 的个数,若 \(1\) 的个数 \(=\) 总面积,则将当前边长的正方形数 \(+ \ 1\),每种边长枚举完了输出边长及其正方形个数。

如果正序枚举则需要一个数组来保存。

#include<bits/stdc++.h>
using namespace std;

int n,sum[310][310],ans[310];

int main(){
	cin>>n;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++){
			char ch; cin>>ch;
			sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+(ch=='1');
		}
	for(int i=2;i<=n;i++){
		int a=i*i;
		for(int x=1;x+i-1<=n;x++)
			for(int y=1;y+i-1<=n;y++){
				int xx=x+i-1,yy=y+i-1,s=sum[xx][yy]-sum[xx][y-1]-sum[x-1][yy]+sum[x-1][y-1];
				if(s==a) ans[i]++;
			}
	}
	for(int i=2;i<=n;i++) if(ans[i]) cout<<i<<' '<<ans[i]<<'\n';
	return 0;
}

T4

仅需要在枚举出边长和左上角后,计算出当前面积并更新最优答案即可。

#include<bits/stdc++.h>
using namespace std;

int n,m,c;
int a[1031][1031],sum[1031][1031];
int ans=-1e9,xx,yy;

int main(){
	cin>>n>>m>>c;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			cin>>a[i][j],sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j];
	for(int i=1;i<=n-c+1;i++)
		for(int j=1;j<=m-c+1;j++){
			int x=i+c-1,y=j+c-1,s=sum[x][y]-sum[x][j-1]-sum[i-1][y]+sum[i-1][j-1];
			if(s>ans)
				ans=s,xx=i,yy=j;
		}
	cout<<xx<<' '<<yy;
	return 0;
}

T5

和 T3 几乎一致,只是不需要输出正方形个数。

#include<bits/stdc++.h>
using namespace std;

int n,m,ans=-1e9;
int a[131][131],sum[131][131];

int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			cin>>a[i][j],sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j];
	for(int i=1;i<=min(n,m);i++){
		int ar=i*i;
		for(int j=1;j+i-1<=n;j++){
			for(int k=1;k+i-1<=m;k++){
				int x=j+i-1,y=k+i-1,s=sum[x][y]-sum[x][k-1]-sum[j-1][y]+sum[j-1][k-1];
				if(s==ar) ans=max(ans,i);
			}
		}
	}
	cout<<ans;
	return 0;
}

习题

T6

T1 的双倍经验,只是需要输出 \n !!!

#include<bits/stdc++.h>
#define int long long
using namespace std;

int n,ans=-1e18;
int a[131][131],sum[131][131];

signed main(){
	cin>>n;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			cin>>a[i][j],sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j];
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			for(int ii=i;ii<=n;ii++)
				for(int jj=j;jj<=n;jj++)
					ans=max(ans,sum[ii][jj]-sum[ii][j-1]-sum[i-1][jj]+sum[i-1][j-1]);
	cout<<ans<<'\n';
	return 0;
}

T7

仍然枚举左上和右下,对所有子矩阵中 \(1\) 的数量取 \(\min\) 即可。

另外这题有个坑,就是矩形不一定为 \(a \times b\),还有可能为 \(b \times a\),因此对于两种情况都要枚举一边。

#include<bits/stdc++.h>
using namespace std;

int n,m,x,y,ans=1e9+31;
int a[131][131],sum[131][131];

int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			cin>>a[i][j],sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j];
	cin>>x>>y;
	//cout<<sum[1][2]-sum[1][1]-sum[0][2]+sum[0][1]<<'\n';
	for(int i=1;i+x-1<=n;i++)
		for(int j=1;j+y-1<=m;j++){
			int ii=i+x-1,jj=j+y-1,s=sum[ii][jj]-sum[ii][j-1]-sum[i-1][jj]+sum[i-1][j-1];
			ans=min(ans,s);
		}
	for(int i=1;i+y-1<=n;i++)
		for(int j=1;j+x-1<=m;j++){
			int ii=i+y-1,jj=j+x-1,s=sum[ii][jj]-sum[ii][j-1]-sum[i-1][jj]+sum[i-1][j-1];
			ans=min(ans,s);
		}
	cout<<ans;
	return 0;
}

T8

首先,因为空间十分紧张,所以我们直接向 \(sum\) 数组读入并进行预处理二维前缀和。

然后我们枚举 \(m \times m\) 的矩形的右下角,求出目标的价值总和取 \(\max\) 即为答案。

同时,我们考虑将目标的点的 \((x,y)\) 均加上 \(0.5\),即让其处于格子的中间,使得我们可以不考虑恰好在正方形边上的点。

#include<bits/stdc++.h>
using namespace std;

int n,m,ans;
int maxx,maxy;
int sum[5031][5031];

int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		int x,y,v;
		cin>>x>>y>>v;
		sum[x+1][y+1]+=v;
		maxx=max(maxx,x+1),maxy=max(maxy,y+1);
	}
	for(int i=1;i<=5001;i++)
		for(int j=1;j<=5001;j++)
			sum[i][j]+=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1];
	for(int i=m;i<=5001;i++)
		for(int j=m;j<=5001;j++)
			ans=max(ans,sum[i][j]-sum[i][j-m]-sum[i-m][j]+sum[i-m][j-m]);
	cout<<ans;
	return 0;
}

标签:Living,13,int,sum,枚举,cin,131,ans,Dream
From: https://www.cnblogs.com/XOF-0-0/p/18062513

相关文章

  • Living-Dream 系列笔记 第10期
    本期主要讲解进阶\(\text{DFS}\)。知识点\(\text{DFS}\)求解连通块问题:定义:若一个点集中的所有点都能互达,且与集合外的点无法互达,则称此点集为一个连通块。考查方式:求连通块数量/大小/周长。例题T1在\(\text{DFS}\)函数中传入参数\(x\)和\(str\),分别表示......
  • Living-Dream 系列笔记 第8期
    本期主要讲解的与上期相同。例题T1上课的时候调这个题感觉要吐了\(qwq\)。。。首先读入\(n\)行字符串,可以采取忽略中间无关单词的方式来直接读取\(X\)和\(Y\)。将所有名字存入\(a\)数组,对\(a\)数组按字典序排序后就可以开始\(\text{DFS}\)了,这里建议使用next_pr......
  • Living-Dream 系列笔记 第9期
    模拟赛掉大分(悲T1板子,不讲。T2首先很明显这题是个去重全排列。和模板的区别就是加了一个\(sum\)参数记录目前已经放了几个苹果。当\(x=n+1\)时若\(sum=m\),则更新答案。同时加入一个剪枝:若在搜索过程中\(sum>m\),则直接return结束搜索。#include<bits/stdc++.h>usi......
  • Living-Dream 系列笔记 第6期
    模拟赛。寄。T1对于每次询问,二分查找数组中对应值的原下标即可,因此需要用结构体存储原始数据和原始下标。这当然是比较麻烦的做法。另一种做法则是开一个map替代桶来存储数组中每个元素的下标,对于每个询问输出即可。另外值得注意的是,本题默认询问之间相互独立。时间复杂度......
  • Living-Dream 系列笔记 第7期
    本期主要讲解深度优先搜索\(\text{DFS}\)。知识点种类:全排列。可以想象为填格子。去重全排列,即组合。时间复杂度均为\(O(n!)\)。\(\text{DFS}\)题的特征:求方案总数/最值。数据范围极小(一般\(n\le20\))。无法直接暴力枚举(因为循环嵌套层数不确定)。......
  • Living-Dream 系列笔记 第4期
    本期主要讲解二分答案。知识点使用场景:最小值最大化,或最大值最小化。在限制条件下找最值。与二分查找的区别:L、R均为答案,而非下标。输出:最大化输出L,反之输出R。例题T1二分\(M\)的值,边界为\(L=-1,R=\max{\{a_i\}}\)。每次枚举到一个\(mid\)就对于每个......
  • Living-Dream 系列笔记 第5期
    本期主要讲解二分答案的进阶。例题T1二分需要的秒数,在check函数中对于每件衣服,若其在\(x\)秒内无法自然晒干,则使用烘干机,并令\(sum\)加上使用烘干机的秒数,最后判断\(sum\)是否\(\lex\)即可。\(Trick\):二分边界需要按数据范围尽可能开大,不能开小了。#include<bits/......
  • ubuntu22.04编译创龙T113-i mini的SDK
    ubuntu版本22.04.11.解压安装包拷贝sdk并解压出来,注意安装包较大请预留好硬盘空间2.预安装编译应用先安装如下应用,在编译过程中需要使用到的依赖sudoaptinstallbuild-essentialcmakeflexbisonu-boot-toolsopenssllibssl-devtexinfo3.安装和更换python2编译使......
  • P4139 上帝与集合的正确用法 题解
    传送门我觉得这题最有意思的其实是"最终会固定为一个数"这个结论。扩展欧拉定理:\(a^b\bmodp\),当\(b\ge\varphi(p)\)时,\(a^b\equiva^{b\bmod\varphi(p)+\varphi(p)}\pmodp\)。所以\(2^{2^{2^{\cdots}}}\)可以递归求解。边界条件\(p=1\)。复杂度如何保证?其实就是......
  • MYSQL学习笔记13: DCL权限控制(用户权限操作)
    DCL权限控制查询权限showgrantsfor'用户名'@'主机名';查询某个用户的权限showgrantsfor'hikaru39'@'localhost';授予权限grant权限列表on数据库名.表名to'用户名'@'主机名';授予某个用户权限#all,给予数据库itcast中所有表的权限grantallonitcast......